Project: Identify Customer Segments¶
In this project, you will apply unsupervised learning techniques to identify segments of the population that form the core customer base for a mail-order sales company in Germany. These segments can then be used to direct marketing campaigns towards audiences that will have the highest expected rate of returns. The data that you will use has been provided by our partners at Bertelsmann Arvato Analytics, and represents a real-life data science task.
This notebook will help you complete this task by providing a framework within which you will perform your analysis steps. In each step of the project, you will see some text describing the subtask that you will perform, followed by one or more code cells for you to complete your work. Feel free to add additional code and markdown cells as you go along so that you can explore everything in precise chunks. The code cells provided in the base template will outline only the major tasks, and will usually not be enough to cover all of the minor tasks that comprise it.
It should be noted that while there will be precise guidelines on how you should handle certain tasks in the project, there will also be places where an exact specification is not provided. There will be times in the project where you will need to make and justify your own decisions on how to treat the data. These are places where there may not be only one way to handle the data. In real-life tasks, there may be many valid ways to approach an analysis task. One of the most important things you can do is clearly document your approach so that other scientists can understand the decisions you've made.
At the end of most sections, there will be a Markdown cell labeled Discussion. In these cells, you will report your findings for the completed section, as well as document the decisions that you made in your approach to each subtask. Your project will be evaluated not just on the code used to complete the tasks outlined, but also your communication about your observations and conclusions at each stage.
# import libraries here; add more as necessary
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import sklearn
from sklearn.preprocessing import OneHotEncoder
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
# magic word for producing visualizations in notebook
%matplotlib inline
pd.options.display.max_rows = None
pd.options.display.max_columns = None
Step 0: Load the Data¶
There are four files associated with this project (not including this one):
Udacity_AZDIAS_Subset.csv: Demographics data for the general population of Germany; 891211 persons (rows) x 85 features (columns).Udacity_CUSTOMERS_Subset.csv: Demographics data for customers of a mail-order company; 191652 persons (rows) x 85 features (columns).Data_Dictionary.md: Detailed information file about the features in the provided datasets.AZDIAS_Feature_Summary.csv: Summary of feature attributes for demographics data; 85 features (rows) x 4 columns
Each row of the demographics files represents a single person, but also includes information outside of individuals, including information about their household, building, and neighborhood. You will use this information to cluster the general population into groups with similar demographic properties. Then, you will see how the people in the customers dataset fit into those created clusters. The hope here is that certain clusters are over-represented in the customers data, as compared to the general population; those over-represented clusters will be assumed to be part of the core userbase. This information can then be used for further applications, such as targeting for a marketing campaign.
To start off with, load in the demographics data for the general population into a pandas DataFrame, and do the same for the feature attributes summary. Note for all of the .csv data files in this project: they're semicolon (;) delimited, so you'll need an additional argument in your read_csv() call to read in the data properly. Also, considering the size of the main dataset, it may take some time for it to load completely.
Once the dataset is loaded, it's recommended that you take a little bit of time just browsing the general structure of the dataset and feature summary file. You'll be getting deep into the innards of the cleaning in the first major step of the project, so gaining some general familiarity can help you get your bearings.
# Load in the general demographics data.
azdias_demographics_data = pd.read_csv("Udacity_AZDIAS_Subset.csv", sep=';')
# Load in the feature summary file.
azdias_feature_summary = pd.read_csv("AZDIAS_Feature_Summary.csv", sep=';')
# Check the structure of the data after it's loaded (e.g. print the number of
# rows and columns, print the first few rows).
n_rows_azdias_demographics_data, n_cols_azdias_demographics_data = azdias_demographics_data.shape
n_rows_azdias_feature_summary, n_cols_azdias_feature_summary = azdias_feature_summary.shape
print('='*50)
print(f'azdias demographics data has {n_rows_azdias_demographics_data} rows')
print(f'azdias demographics ata has {n_cols_azdias_demographics_data} columns')
print('='*50)
print(f'azdias feature summary has {n_rows_azdias_feature_summary} rows')
print(f'azdias feature summary {n_cols_azdias_feature_summary} columns')
================================================== azdias demographics data has 891221 rows azdias demographics ata has 85 columns ================================================== azdias feature summary has 85 rows azdias feature summary 4 columns
Tip: Add additional cells to keep everything in reasonably-sized chunks! Keyboard shortcut
esc --> a(press escape to enter command mode, then press the 'A' key) adds a new cell before the active cell, andesc --> badds a new cell after the active cell. If you need to convert an active cell to a markdown cell, useesc --> mand to convert to a code cell, useesc --> y.
Step 1: Preprocessing¶
Step 1.1: Assess Missing Data¶
The feature summary file contains a summary of properties for each demographics data column. You will use this file to help you make cleaning decisions during this stage of the project. First of all, you should assess the demographics data in terms of missing data. Pay attention to the following points as you perform your analysis, and take notes on what you observe. Make sure that you fill in the Discussion cell with your findings and decisions at the end of each step that has one!
Step 1.1.1: Convert Missing Value Codes to NaNs¶
The fourth column of the feature attributes summary (loaded in above as feat_info) documents the codes from the data dictionary that indicate missing or unknown data. While the file encodes this as a list (e.g. [-1,0]), this will get read in as a string object. You'll need to do a little bit of parsing to make use of it to identify and clean the data. Convert data that matches a 'missing' or 'unknown' value code into a numpy NaN value. You might want to see how much data takes on a 'missing' or 'unknown' code, and how much data is naturally missing, as a point of interest.
As one more reminder, you are encouraged to add additional cells to break up your analysis into manageable chunks.
azdias_demographics_data.head()
| AGER_TYP | ALTERSKATEGORIE_GROB | ANREDE_KZ | CJT_GESAMTTYP | FINANZ_MINIMALIST | FINANZ_SPARER | FINANZ_VORSORGER | FINANZ_ANLEGER | FINANZ_UNAUFFAELLIGER | FINANZ_HAUSBAUER | FINANZTYP | GEBURTSJAHR | GFK_URLAUBERTYP | GREEN_AVANTGARDE | HEALTH_TYP | LP_LEBENSPHASE_FEIN | LP_LEBENSPHASE_GROB | LP_FAMILIE_FEIN | LP_FAMILIE_GROB | LP_STATUS_FEIN | LP_STATUS_GROB | NATIONALITAET_KZ | PRAEGENDE_JUGENDJAHRE | RETOURTYP_BK_S | SEMIO_SOZ | SEMIO_FAM | SEMIO_REL | SEMIO_MAT | SEMIO_VERT | SEMIO_LUST | SEMIO_ERL | SEMIO_KULT | SEMIO_RAT | SEMIO_KRIT | SEMIO_DOM | SEMIO_KAEM | SEMIO_PFLICHT | SEMIO_TRADV | SHOPPER_TYP | SOHO_KZ | TITEL_KZ | VERS_TYP | ZABEOTYP | ALTER_HH | ANZ_PERSONEN | ANZ_TITEL | HH_EINKOMMEN_SCORE | KK_KUNDENTYP | W_KEIT_KIND_HH | WOHNDAUER_2008 | ANZ_HAUSHALTE_AKTIV | ANZ_HH_TITEL | GEBAEUDETYP | KONSUMNAEHE | MIN_GEBAEUDEJAHR | OST_WEST_KZ | WOHNLAGE | CAMEO_DEUG_2015 | CAMEO_DEU_2015 | CAMEO_INTL_2015 | KBA05_ANTG1 | KBA05_ANTG2 | KBA05_ANTG3 | KBA05_ANTG4 | KBA05_BAUMAX | KBA05_GBZ | BALLRAUM | EWDICHTE | INNENSTADT | GEBAEUDETYP_RASTER | KKK | MOBI_REGIO | ONLINE_AFFINITAET | REGIOTYP | KBA13_ANZAHL_PKW | PLZ8_ANTG1 | PLZ8_ANTG2 | PLZ8_ANTG3 | PLZ8_ANTG4 | PLZ8_BAUMAX | PLZ8_HHZ | PLZ8_GBZ | ARBEIT | ORTSGR_KLS9 | RELAT_AB | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | -1 | 2 | 1 | 2.0 | 3 | 4 | 3 | 5 | 5 | 3 | 4 | 0 | 10.0 | 0 | -1 | 15.0 | 4.0 | 2.0 | 2.0 | 1.0 | 1.0 | 0 | 0 | 5.0 | 2 | 6 | 7 | 5 | 1 | 5 | 3 | 3 | 4 | 7 | 6 | 6 | 5 | 3 | -1 | NaN | NaN | -1 | 3 | NaN | NaN | NaN | 2.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 1 | -1 | 1 | 2 | 5.0 | 1 | 5 | 2 | 5 | 4 | 5 | 1 | 1996 | 10.0 | 0 | 3 | 21.0 | 6.0 | 5.0 | 3.0 | 2.0 | 1.0 | 1 | 14 | 1.0 | 5 | 4 | 4 | 3 | 1 | 2 | 2 | 3 | 6 | 4 | 7 | 4 | 7 | 6 | 3 | 1.0 | 0.0 | 2 | 5 | 0.0 | 2.0 | 0.0 | 6.0 | NaN | 3.0 | 9.0 | 11.0 | 0.0 | 8.0 | 1.0 | 1992.0 | W | 4.0 | 8 | 8A | 51 | 0.0 | 0.0 | 0.0 | 2.0 | 5.0 | 1.0 | 6.0 | 3.0 | 8.0 | 3.0 | 2.0 | 1.0 | 3.0 | 3.0 | 963.0 | 2.0 | 3.0 | 2.0 | 1.0 | 1.0 | 5.0 | 4.0 | 3.0 | 5.0 | 4.0 |
| 2 | -1 | 3 | 2 | 3.0 | 1 | 4 | 1 | 2 | 3 | 5 | 1 | 1979 | 10.0 | 1 | 3 | 3.0 | 1.0 | 1.0 | 1.0 | 3.0 | 2.0 | 1 | 15 | 3.0 | 4 | 1 | 3 | 3 | 4 | 4 | 6 | 3 | 4 | 7 | 7 | 7 | 3 | 3 | 2 | 0.0 | 0.0 | 1 | 5 | 17.0 | 1.0 | 0.0 | 4.0 | NaN | 3.0 | 9.0 | 10.0 | 0.0 | 1.0 | 5.0 | 1992.0 | W | 2.0 | 4 | 4C | 24 | 1.0 | 3.0 | 1.0 | 0.0 | 0.0 | 3.0 | 2.0 | 4.0 | 4.0 | 4.0 | 2.0 | 3.0 | 2.0 | 2.0 | 712.0 | 3.0 | 3.0 | 1.0 | 0.0 | 1.0 | 4.0 | 4.0 | 3.0 | 5.0 | 2.0 |
| 3 | 2 | 4 | 2 | 2.0 | 4 | 2 | 5 | 2 | 1 | 2 | 6 | 1957 | 1.0 | 0 | 2 | 0.0 | 0.0 | 0.0 | 0.0 | 9.0 | 4.0 | 1 | 8 | 2.0 | 5 | 1 | 2 | 1 | 4 | 4 | 7 | 4 | 3 | 4 | 4 | 5 | 4 | 4 | 1 | 0.0 | 0.0 | 1 | 3 | 13.0 | 0.0 | 0.0 | 1.0 | NaN | NaN | 9.0 | 1.0 | 0.0 | 1.0 | 4.0 | 1997.0 | W | 7.0 | 2 | 2A | 12 | 4.0 | 1.0 | 0.0 | 0.0 | 1.0 | 4.0 | 4.0 | 2.0 | 6.0 | 4.0 | 0.0 | 4.0 | 1.0 | 0.0 | 596.0 | 2.0 | 2.0 | 2.0 | 0.0 | 1.0 | 3.0 | 4.0 | 2.0 | 3.0 | 3.0 |
| 4 | -1 | 3 | 1 | 5.0 | 4 | 3 | 4 | 1 | 3 | 2 | 5 | 1963 | 5.0 | 0 | 3 | 32.0 | 10.0 | 10.0 | 5.0 | 3.0 | 2.0 | 1 | 8 | 5.0 | 6 | 4 | 4 | 2 | 7 | 4 | 4 | 6 | 2 | 3 | 2 | 2 | 4 | 2 | 2 | 0.0 | 0.0 | 2 | 4 | 20.0 | 4.0 | 0.0 | 5.0 | 1.0 | 2.0 | 9.0 | 3.0 | 0.0 | 1.0 | 4.0 | 1992.0 | W | 3.0 | 6 | 6B | 43 | 1.0 | 4.0 | 1.0 | 0.0 | 0.0 | 3.0 | 2.0 | 5.0 | 1.0 | 5.0 | 3.0 | 3.0 | 5.0 | 5.0 | 435.0 | 2.0 | 4.0 | 2.0 | 1.0 | 2.0 | 3.0 | 3.0 | 4.0 | 6.0 | 5.0 |
azdias_demographics_data.describe()
| AGER_TYP | ALTERSKATEGORIE_GROB | ANREDE_KZ | CJT_GESAMTTYP | FINANZ_MINIMALIST | FINANZ_SPARER | FINANZ_VORSORGER | FINANZ_ANLEGER | FINANZ_UNAUFFAELLIGER | FINANZ_HAUSBAUER | FINANZTYP | GEBURTSJAHR | GFK_URLAUBERTYP | GREEN_AVANTGARDE | HEALTH_TYP | LP_LEBENSPHASE_FEIN | LP_LEBENSPHASE_GROB | LP_FAMILIE_FEIN | LP_FAMILIE_GROB | LP_STATUS_FEIN | LP_STATUS_GROB | NATIONALITAET_KZ | PRAEGENDE_JUGENDJAHRE | RETOURTYP_BK_S | SEMIO_SOZ | SEMIO_FAM | SEMIO_REL | SEMIO_MAT | SEMIO_VERT | SEMIO_LUST | SEMIO_ERL | SEMIO_KULT | SEMIO_RAT | SEMIO_KRIT | SEMIO_DOM | SEMIO_KAEM | SEMIO_PFLICHT | SEMIO_TRADV | SHOPPER_TYP | SOHO_KZ | TITEL_KZ | VERS_TYP | ZABEOTYP | ALTER_HH | ANZ_PERSONEN | ANZ_TITEL | HH_EINKOMMEN_SCORE | KK_KUNDENTYP | W_KEIT_KIND_HH | WOHNDAUER_2008 | ANZ_HAUSHALTE_AKTIV | ANZ_HH_TITEL | GEBAEUDETYP | KONSUMNAEHE | MIN_GEBAEUDEJAHR | WOHNLAGE | KBA05_ANTG1 | KBA05_ANTG2 | KBA05_ANTG3 | KBA05_ANTG4 | KBA05_BAUMAX | KBA05_GBZ | BALLRAUM | EWDICHTE | INNENSTADT | GEBAEUDETYP_RASTER | KKK | MOBI_REGIO | ONLINE_AFFINITAET | REGIOTYP | KBA13_ANZAHL_PKW | PLZ8_ANTG1 | PLZ8_ANTG2 | PLZ8_ANTG3 | PLZ8_ANTG4 | PLZ8_BAUMAX | PLZ8_HHZ | PLZ8_GBZ | ARBEIT | ORTSGR_KLS9 | RELAT_AB | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 891221.000000 | 891221.000000 | 891221.000000 | 886367.000000 | 891221.000000 | 891221.000000 | 891221.000000 | 891221.000000 | 891221.000000 | 891221.000000 | 891221.000000 | 891221.000000 | 886367.000000 | 891221.000000 | 891221.000000 | 886367.000000 | 886367.000000 | 886367.000000 | 886367.000000 | 886367.000000 | 886367.000000 | 891221.000000 | 891221.000000 | 886367.000000 | 891221.000000 | 891221.000000 | 891221.000000 | 891221.000000 | 891221.000000 | 891221.000000 | 891221.000000 | 891221.000000 | 891221.000000 | 891221.000000 | 891221.000000 | 891221.000000 | 891221.000000 | 891221.000000 | 891221.000000 | 817722.000000 | 817722.000000 | 891221.000000 | 891221.000000 | 817722.000000 | 817722.000000 | 817722.000000 | 872873.000000 | 306609.000000 | 783619.000000 | 817722.000000 | 798073.000000 | 794213.000000 | 798073.000000 | 817252.000000 | 798073.000000 | 798073.000000 | 757897.000000 | 757897.000000 | 757897.000000 | 757897.000000 | 757897.000000 | 757897.000000 | 797481.000000 | 797481.000000 | 797481.000000 | 798066.000000 | 770025.000000 | 757897.000000 | 886367.000000 | 770025.000000 | 785421.000000 | 774706.000000 | 774706.000000 | 774706.000000 | 774706.000000 | 774706.000000 | 774706.000000 | 774706.000000 | 794005.000000 | 794005.000000 | 794005.00000 |
| mean | -0.358435 | 2.777398 | 1.522098 | 3.632838 | 3.074528 | 2.821039 | 3.401106 | 3.033328 | 2.874167 | 3.075121 | 3.790586 | 1101.178533 | 7.350304 | 0.196612 | 1.792102 | 14.622637 | 4.453621 | 3.599574 | 2.185966 | 4.791151 | 2.432575 | 1.026827 | 8.154346 | 3.419630 | 3.945860 | 4.272729 | 4.240609 | 4.001597 | 4.023709 | 4.359086 | 4.481405 | 4.025014 | 3.910139 | 4.763223 | 4.667550 | 4.445007 | 4.256076 | 3.661784 | 1.266967 | 0.008423 | 0.003483 | 1.197852 | 3.362438 | 10.864126 | 1.727637 | 0.004162 | 4.207243 | 3.410640 | 3.933406 | 7.908791 | 8.287263 | 0.040647 | 2.798641 | 3.018452 | 1993.277011 | 4.052836 | 1.494277 | 1.265584 | 0.624525 | 0.305927 | 1.389552 | 3.158580 | 4.153043 | 3.939172 | 4.549491 | 3.738306 | 2.592991 | 2.963540 | 2.698691 | 4.257967 | 619.701439 | 2.253330 | 2.801858 | 1.595426 | 0.699166 | 1.943913 | 3.612821 | 3.381087 | 3.167854 | 5.293002 | 3.07222 |
| std | 1.198724 | 1.068775 | 0.499512 | 1.595021 | 1.321055 | 1.464749 | 1.322134 | 1.529603 | 1.486731 | 1.353248 | 1.987876 | 976.583551 | 3.525723 | 0.397437 | 1.269062 | 12.616883 | 3.855639 | 3.926486 | 1.756537 | 3.425305 | 1.474315 | 0.586634 | 4.844532 | 1.417741 | 1.946564 | 1.915885 | 2.007373 | 1.857540 | 2.077746 | 2.022829 | 1.807552 | 1.903816 | 1.580306 | 1.830789 | 1.795712 | 1.852412 | 1.770137 | 1.707637 | 1.287435 | 0.091392 | 0.084957 | 0.952532 | 1.352704 | 7.639683 | 1.155849 | 0.068855 | 1.624057 | 1.628844 | 1.964701 | 1.923137 | 15.628087 | 0.324028 | 2.656713 | 1.550312 | 3.332739 | 1.949539 | 1.403961 | 1.245178 | 1.013443 | 0.638725 | 1.779483 | 1.329537 | 2.183710 | 1.718996 | 2.028919 | 0.923193 | 1.119052 | 1.428882 | 1.521524 | 2.030385 | 340.034318 | 0.972008 | 0.920309 | 0.986736 | 0.727137 | 1.459654 | 0.973967 | 1.111598 | 1.002376 | 2.303739 | 1.36298 |
| min | -1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 0.000000 | 1.000000 | 0.000000 | -1.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | 1.000000 | 0.000000 | 0.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | -1.000000 | 0.000000 | 0.000000 | -1.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | 1.000000 | 0.000000 | 1.000000 | 0.000000 | 0.000000 | 1.000000 | 1.000000 | 1985.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 0.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 0.000000 | 1.00000 |
| 25% | -1.000000 | 2.000000 | 1.000000 | 2.000000 | 2.000000 | 1.000000 | 3.000000 | 2.000000 | 2.000000 | 2.000000 | 2.000000 | 0.000000 | 5.000000 | 0.000000 | 1.000000 | 4.000000 | 1.000000 | 1.000000 | 1.000000 | 2.000000 | 1.000000 | 1.000000 | 5.000000 | 2.000000 | 2.000000 | 3.000000 | 3.000000 | 2.000000 | 2.000000 | 2.000000 | 3.000000 | 3.000000 | 3.000000 | 3.000000 | 3.000000 | 3.000000 | 3.000000 | 2.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | 3.000000 | 0.000000 | 1.000000 | 0.000000 | 3.000000 | 2.000000 | 2.000000 | 8.000000 | 1.000000 | 0.000000 | 1.000000 | 2.000000 | 1992.000000 | 3.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 2.000000 | 2.000000 | 2.000000 | 3.000000 | 3.000000 | 2.000000 | 2.000000 | 1.000000 | 3.000000 | 384.000000 | 1.000000 | 2.000000 | 1.000000 | 0.000000 | 1.000000 | 3.000000 | 3.000000 | 3.000000 | 4.000000 | 2.00000 |
| 50% | -1.000000 | 3.000000 | 2.000000 | 4.000000 | 3.000000 | 3.000000 | 3.000000 | 3.000000 | 3.000000 | 3.000000 | 4.000000 | 1943.000000 | 8.000000 | 0.000000 | 2.000000 | 11.000000 | 3.000000 | 1.000000 | 1.000000 | 4.000000 | 2.000000 | 1.000000 | 8.000000 | 3.000000 | 4.000000 | 4.000000 | 4.000000 | 4.000000 | 4.000000 | 5.000000 | 4.000000 | 4.000000 | 4.000000 | 5.000000 | 5.000000 | 5.000000 | 4.000000 | 3.000000 | 1.000000 | 0.000000 | 0.000000 | 1.000000 | 3.000000 | 13.000000 | 1.000000 | 0.000000 | 5.000000 | 3.000000 | 4.000000 | 9.000000 | 4.000000 | 0.000000 | 1.000000 | 3.000000 | 1992.000000 | 3.000000 | 1.000000 | 1.000000 | 0.000000 | 0.000000 | 1.000000 | 3.000000 | 5.000000 | 4.000000 | 5.000000 | 4.000000 | 3.000000 | 3.000000 | 3.000000 | 5.000000 | 549.000000 | 2.000000 | 3.000000 | 2.000000 | 1.000000 | 1.000000 | 4.000000 | 3.000000 | 3.000000 | 5.000000 | 3.00000 |
| 75% | -1.000000 | 4.000000 | 2.000000 | 5.000000 | 4.000000 | 4.000000 | 5.000000 | 5.000000 | 4.000000 | 4.000000 | 6.000000 | 1970.000000 | 10.000000 | 0.000000 | 3.000000 | 27.000000 | 8.000000 | 8.000000 | 4.000000 | 9.000000 | 4.000000 | 1.000000 | 14.000000 | 5.000000 | 6.000000 | 6.000000 | 6.000000 | 5.000000 | 6.000000 | 6.000000 | 6.000000 | 5.000000 | 5.000000 | 6.000000 | 6.000000 | 6.000000 | 6.000000 | 5.000000 | 2.000000 | 0.000000 | 0.000000 | 2.000000 | 4.000000 | 17.000000 | 2.000000 | 0.000000 | 6.000000 | 5.000000 | 6.000000 | 9.000000 | 9.000000 | 0.000000 | 3.000000 | 4.000000 | 1993.000000 | 5.000000 | 3.000000 | 2.000000 | 1.000000 | 0.000000 | 3.000000 | 4.000000 | 6.000000 | 6.000000 | 6.000000 | 4.000000 | 3.000000 | 4.000000 | 4.000000 | 6.000000 | 778.000000 | 3.000000 | 3.000000 | 2.000000 | 1.000000 | 3.000000 | 4.000000 | 4.000000 | 4.000000 | 7.000000 | 4.00000 |
| max | 3.000000 | 9.000000 | 2.000000 | 6.000000 | 5.000000 | 5.000000 | 5.000000 | 5.000000 | 5.000000 | 5.000000 | 6.000000 | 2017.000000 | 12.000000 | 1.000000 | 3.000000 | 40.000000 | 12.000000 | 11.000000 | 5.000000 | 10.000000 | 5.000000 | 3.000000 | 15.000000 | 5.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 3.000000 | 1.000000 | 5.000000 | 2.000000 | 6.000000 | 21.000000 | 45.000000 | 6.000000 | 6.000000 | 6.000000 | 6.000000 | 9.000000 | 595.000000 | 23.000000 | 8.000000 | 7.000000 | 2016.000000 | 8.000000 | 4.000000 | 4.000000 | 3.000000 | 2.000000 | 5.000000 | 5.000000 | 7.000000 | 6.000000 | 8.000000 | 5.000000 | 4.000000 | 6.000000 | 5.000000 | 7.000000 | 2300.000000 | 4.000000 | 4.000000 | 3.000000 | 2.000000 | 5.000000 | 5.000000 | 5.000000 | 9.000000 | 9.000000 | 9.00000 |
azdias_feature_summary.head()
| attribute | information_level | type | missing_or_unknown | |
|---|---|---|---|---|
| 0 | AGER_TYP | person | categorical | [-1,0] |
| 1 | ALTERSKATEGORIE_GROB | person | ordinal | [-1,0,9] |
| 2 | ANREDE_KZ | person | categorical | [-1,0] |
| 3 | CJT_GESAMTTYP | person | categorical | [0] |
| 4 | FINANZ_MINIMALIST | person | ordinal | [-1] |
azdias_feature_summary.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 85 entries, 0 to 84 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 attribute 85 non-null object 1 information_level 85 non-null object 2 type 85 non-null object 3 missing_or_unknown 85 non-null object dtypes: object(4) memory usage: 2.8+ KB
def convert_str_to_list(string_list):
result_lits = []
string_list = string_list[1:-1] #remove the opening and closing brackets [ ]
string_values = string_list.split(',')
for val in string_values:
try:
result_lits.append(int(val))
except:
result_lits.append(val)
return result_lits
missing_values = azdias_feature_summary['missing_or_unknown'].apply(convert_str_to_list)
for attribute, missing_vals in zip(azdias_feature_summary['attribute'], missing_values):
if len(missing_vals)>0:
for missing_value in missing_vals:
azdias_demographics_data.loc[azdias_demographics_data[attribute] == missing_value, attribute] = np.nan
Step 1.1.2: Assess Missing Data in Each Column¶
How much missing data is present in each column? There are a few columns that are outliers in terms of the proportion of values that are missing. You will want to use matplotlib's hist() function to visualize the distribution of missing value counts to find these columns. Identify and document these columns. While some of these columns might have justifications for keeping or re-encoding the data, for this project you should just remove them from the dataframe. (Feel free to make remarks about these outlier columns in the discussion, however!)
For the remaining features, are there any patterns in which columns have, or share, missing data?
nans_values = (azdias_demographics_data.isna().sum()/azdias_demographics_data.shape[0]*100).sort_values(ascending=True)
nans_values
ZABEOTYP 0.000000 SEMIO_TRADV 0.000000 SEMIO_PFLICHT 0.000000 SEMIO_KAEM 0.000000 SEMIO_DOM 0.000000 SEMIO_KRIT 0.000000 SEMIO_RAT 0.000000 SEMIO_KULT 0.000000 SEMIO_ERL 0.000000 SEMIO_LUST 0.000000 SEMIO_VERT 0.000000 SEMIO_MAT 0.000000 SEMIO_REL 0.000000 SEMIO_SOZ 0.000000 SEMIO_FAM 0.000000 FINANZ_VORSORGER 0.000000 ANREDE_KZ 0.000000 FINANZ_MINIMALIST 0.000000 FINANZ_SPARER 0.000000 FINANZ_ANLEGER 0.000000 FINANZ_UNAUFFAELLIGER 0.000000 FINANZ_HAUSBAUER 0.000000 GREEN_AVANTGARDE 0.000000 FINANZTYP 0.000000 ALTERSKATEGORIE_GROB 0.323264 GFK_URLAUBERTYP 0.544646 LP_STATUS_GROB 0.544646 LP_STATUS_FEIN 0.544646 ONLINE_AFFINITAET 0.544646 RETOURTYP_BK_S 0.544646 CJT_GESAMTTYP 0.544646 HH_EINKOMMEN_SCORE 2.058749 WOHNDAUER_2008 8.247000 ANZ_TITEL 8.247000 SOHO_KZ 8.247000 ANZ_PERSONEN 8.247000 KONSUMNAEHE 8.299737 LP_FAMILIE_GROB 8.728699 LP_FAMILIE_FEIN 8.728699 OST_WEST_KZ 10.451729 WOHNLAGE 10.451729 GEBAEUDETYP 10.451729 MIN_GEBAEUDEJAHR 10.451729 GEBAEUDETYP_RASTER 10.452514 BALLRAUM 10.518154 EWDICHTE 10.518154 INNENSTADT 10.518154 LP_LEBENSPHASE_GROB 10.611509 ANZ_HH_TITEL 10.884842 ORTSGR_KLS9 10.914689 ARBEIT 10.926022 RELAT_AB 10.926022 LP_LEBENSPHASE_FEIN 10.954859 CAMEO_DEUG_2015 11.147852 CAMEO_DEU_2015 11.147852 CAMEO_INTL_2015 11.147852 ANZ_HAUSHALTE_AKTIV 11.176913 KBA13_ANZAHL_PKW 11.871354 PRAEGENDE_JUGENDJAHRE 12.136608 NATIONALITAET_KZ 12.153551 HEALTH_TYP 12.476816 VERS_TYP 12.476816 SHOPPER_TYP 12.476816 PLZ8_ANTG2 13.073637 PLZ8_ANTG3 13.073637 PLZ8_ANTG1 13.073637 PLZ8_ANTG4 13.073637 PLZ8_BAUMAX 13.073637 PLZ8_HHZ 13.073637 PLZ8_GBZ 13.073637 KBA05_ANTG3 14.959701 KBA05_ANTG2 14.959701 KBA05_ANTG1 14.959701 MOBI_REGIO 14.959701 KBA05_GBZ 14.959701 KBA05_ANTG4 14.959701 W_KEIT_KIND_HH 16.605084 KKK 17.735668 REGIOTYP 17.735668 ALTER_HH 34.813699 GEBURTSJAHR 44.020282 KBA05_BAUMAX 53.468668 KK_KUNDENTYP 65.596749 AGER_TYP 76.955435 TITEL_KZ 99.757636 dtype: float64
# Perform an assessment of how much missing data there is in each column of the
# dataset.
plt.figure(figsize=(20, 20))
sns.set_style("darkgrid")
missing_values = (azdias_demographics_data.isnull().sum()/azdias_demographics_data.shape[0]*100).sort_values(ascending=False).reset_index()
missing_values.columns = ['Column', 'Missing Values']
sns.barplot(data=missing_values, x='Column', y='Missing Values', hue='Column', palette='viridis', legend=False)
plt.title('Missing Values per Column', fontsize=14)
plt.xlabel('Columns', fontsize=12)
plt.ylabel('Count of Missing Values', fontsize=12)
plt.xticks(rotation=45, ha='right') # Rotate column names for readability
plt.tight_layout()
azdias_demographics_data.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 891221 entries, 0 to 891220 Data columns (total 85 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 AGER_TYP 205378 non-null float64 1 ALTERSKATEGORIE_GROB 888340 non-null float64 2 ANREDE_KZ 891221 non-null float64 3 CJT_GESAMTTYP 886367 non-null float64 4 FINANZ_MINIMALIST 891221 non-null float64 5 FINANZ_SPARER 891221 non-null float64 6 FINANZ_VORSORGER 891221 non-null float64 7 FINANZ_ANLEGER 891221 non-null float64 8 FINANZ_UNAUFFAELLIGER 891221 non-null float64 9 FINANZ_HAUSBAUER 891221 non-null float64 10 FINANZTYP 891221 non-null float64 11 GEBURTSJAHR 498903 non-null float64 12 GFK_URLAUBERTYP 886367 non-null float64 13 GREEN_AVANTGARDE 891221 non-null float64 14 HEALTH_TYP 780025 non-null float64 15 LP_LEBENSPHASE_FEIN 793589 non-null float64 16 LP_LEBENSPHASE_GROB 796649 non-null float64 17 LP_FAMILIE_FEIN 813429 non-null float64 18 LP_FAMILIE_GROB 813429 non-null float64 19 LP_STATUS_FEIN 886367 non-null float64 20 LP_STATUS_GROB 886367 non-null float64 21 NATIONALITAET_KZ 782906 non-null float64 22 PRAEGENDE_JUGENDJAHRE 783057 non-null float64 23 RETOURTYP_BK_S 886367 non-null float64 24 SEMIO_SOZ 891221 non-null float64 25 SEMIO_FAM 891221 non-null float64 26 SEMIO_REL 891221 non-null float64 27 SEMIO_MAT 891221 non-null float64 28 SEMIO_VERT 891221 non-null float64 29 SEMIO_LUST 891221 non-null float64 30 SEMIO_ERL 891221 non-null float64 31 SEMIO_KULT 891221 non-null float64 32 SEMIO_RAT 891221 non-null float64 33 SEMIO_KRIT 891221 non-null float64 34 SEMIO_DOM 891221 non-null float64 35 SEMIO_KAEM 891221 non-null float64 36 SEMIO_PFLICHT 891221 non-null float64 37 SEMIO_TRADV 891221 non-null float64 38 SHOPPER_TYP 780025 non-null float64 39 SOHO_KZ 817722 non-null float64 40 TITEL_KZ 2160 non-null float64 41 VERS_TYP 780025 non-null float64 42 ZABEOTYP 891221 non-null float64 43 ALTER_HH 580954 non-null float64 44 ANZ_PERSONEN 817722 non-null float64 45 ANZ_TITEL 817722 non-null float64 46 HH_EINKOMMEN_SCORE 872873 non-null float64 47 KK_KUNDENTYP 306609 non-null float64 48 W_KEIT_KIND_HH 743233 non-null float64 49 WOHNDAUER_2008 817722 non-null float64 50 ANZ_HAUSHALTE_AKTIV 791610 non-null float64 51 ANZ_HH_TITEL 794213 non-null float64 52 GEBAEUDETYP 798073 non-null float64 53 KONSUMNAEHE 817252 non-null float64 54 MIN_GEBAEUDEJAHR 798073 non-null float64 55 OST_WEST_KZ 798073 non-null object 56 WOHNLAGE 798073 non-null float64 57 CAMEO_DEUG_2015 791869 non-null object 58 CAMEO_DEU_2015 791869 non-null object 59 CAMEO_INTL_2015 791869 non-null object 60 KBA05_ANTG1 757897 non-null float64 61 KBA05_ANTG2 757897 non-null float64 62 KBA05_ANTG3 757897 non-null float64 63 KBA05_ANTG4 757897 non-null float64 64 KBA05_BAUMAX 414697 non-null float64 65 KBA05_GBZ 757897 non-null float64 66 BALLRAUM 797481 non-null float64 67 EWDICHTE 797481 non-null float64 68 INNENSTADT 797481 non-null float64 69 GEBAEUDETYP_RASTER 798066 non-null float64 70 KKK 733157 non-null float64 71 MOBI_REGIO 757897 non-null float64 72 ONLINE_AFFINITAET 886367 non-null float64 73 REGIOTYP 733157 non-null float64 74 KBA13_ANZAHL_PKW 785421 non-null float64 75 PLZ8_ANTG1 774706 non-null float64 76 PLZ8_ANTG2 774706 non-null float64 77 PLZ8_ANTG3 774706 non-null float64 78 PLZ8_ANTG4 774706 non-null float64 79 PLZ8_BAUMAX 774706 non-null float64 80 PLZ8_HHZ 774706 non-null float64 81 PLZ8_GBZ 774706 non-null float64 82 ARBEIT 793846 non-null float64 83 ORTSGR_KLS9 793947 non-null float64 84 RELAT_AB 793846 non-null float64 dtypes: float64(81), object(4) memory usage: 578.0+ MB
sns.distplot(nans_values)
plt.xlabel('percentage of missing values for each column')
plt.ylabel('Counts')
plt.title('Histogram for number of missing values')
plt.show()
/tmp/ipykernel_13/3719466440.py:1: UserWarning: `distplot` is a deprecated function and will be removed in seaborn v0.14.0. Please adapt your code to use either `displot` (a figure-level function with similar flexibility) or `histplot` (an axes-level function for histograms). For a guide to updating your code to use the new functions, please see https://gist.github.com/mwaskom/de44147ed2974457ad6372750bbe5751 sns.distplot(nans_values)
# Investigate patterns in the amount of missing data in each column.
droping_columns = nans_values[nans_values>20].index
droping_columns
Index(['ALTER_HH', 'GEBURTSJAHR', 'KBA05_BAUMAX', 'KK_KUNDENTYP', 'AGER_TYP',
'TITEL_KZ'],
dtype='object')
# Remove the outlier columns from the dataset. (You'll perform other data
# engineering tasks such as re-encoding and imputation later.)
azdias_demographics_data.drop(columns=droping_columns, axis=1, inplace=True)
azdias_demographics_data.head()
| ALTERSKATEGORIE_GROB | ANREDE_KZ | CJT_GESAMTTYP | FINANZ_MINIMALIST | FINANZ_SPARER | FINANZ_VORSORGER | FINANZ_ANLEGER | FINANZ_UNAUFFAELLIGER | FINANZ_HAUSBAUER | FINANZTYP | GFK_URLAUBERTYP | GREEN_AVANTGARDE | HEALTH_TYP | LP_LEBENSPHASE_FEIN | LP_LEBENSPHASE_GROB | LP_FAMILIE_FEIN | LP_FAMILIE_GROB | LP_STATUS_FEIN | LP_STATUS_GROB | NATIONALITAET_KZ | PRAEGENDE_JUGENDJAHRE | RETOURTYP_BK_S | SEMIO_SOZ | SEMIO_FAM | SEMIO_REL | SEMIO_MAT | SEMIO_VERT | SEMIO_LUST | SEMIO_ERL | SEMIO_KULT | SEMIO_RAT | SEMIO_KRIT | SEMIO_DOM | SEMIO_KAEM | SEMIO_PFLICHT | SEMIO_TRADV | SHOPPER_TYP | SOHO_KZ | VERS_TYP | ZABEOTYP | ANZ_PERSONEN | ANZ_TITEL | HH_EINKOMMEN_SCORE | W_KEIT_KIND_HH | WOHNDAUER_2008 | ANZ_HAUSHALTE_AKTIV | ANZ_HH_TITEL | GEBAEUDETYP | KONSUMNAEHE | MIN_GEBAEUDEJAHR | OST_WEST_KZ | WOHNLAGE | CAMEO_DEUG_2015 | CAMEO_DEU_2015 | CAMEO_INTL_2015 | KBA05_ANTG1 | KBA05_ANTG2 | KBA05_ANTG3 | KBA05_ANTG4 | KBA05_GBZ | BALLRAUM | EWDICHTE | INNENSTADT | GEBAEUDETYP_RASTER | KKK | MOBI_REGIO | ONLINE_AFFINITAET | REGIOTYP | KBA13_ANZAHL_PKW | PLZ8_ANTG1 | PLZ8_ANTG2 | PLZ8_ANTG3 | PLZ8_ANTG4 | PLZ8_BAUMAX | PLZ8_HHZ | PLZ8_GBZ | ARBEIT | ORTSGR_KLS9 | RELAT_AB | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2.0 | 1.0 | 2.0 | 3.0 | 4.0 | 3.0 | 5.0 | 5.0 | 3.0 | 4.0 | 10.0 | 0.0 | NaN | 15.0 | 4.0 | 2.0 | 2.0 | 1.0 | 1.0 | NaN | NaN | 5.0 | 2.0 | 6.0 | 7.0 | 5.0 | 1.0 | 5.0 | 3.0 | 3.0 | 4.0 | 7.0 | 6.0 | 6.0 | 5.0 | 3.0 | NaN | NaN | NaN | 3.0 | NaN | NaN | 2.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 1 | 1.0 | 2.0 | 5.0 | 1.0 | 5.0 | 2.0 | 5.0 | 4.0 | 5.0 | 1.0 | 10.0 | 0.0 | 3.0 | 21.0 | 6.0 | 5.0 | 3.0 | 2.0 | 1.0 | 1.0 | 14.0 | 1.0 | 5.0 | 4.0 | 4.0 | 3.0 | 1.0 | 2.0 | 2.0 | 3.0 | 6.0 | 4.0 | 7.0 | 4.0 | 7.0 | 6.0 | 3.0 | 1.0 | 2.0 | 5.0 | 2.0 | 0.0 | 6.0 | 3.0 | 9.0 | 11.0 | 0.0 | 8.0 | 1.0 | 1992.0 | W | 4.0 | 8 | 8A | 51 | 0.0 | 0.0 | 0.0 | 2.0 | 1.0 | 6.0 | 3.0 | 8.0 | 3.0 | 2.0 | 1.0 | 3.0 | 3.0 | 963.0 | 2.0 | 3.0 | 2.0 | 1.0 | 1.0 | 5.0 | 4.0 | 3.0 | 5.0 | 4.0 |
| 2 | 3.0 | 2.0 | 3.0 | 1.0 | 4.0 | 1.0 | 2.0 | 3.0 | 5.0 | 1.0 | 10.0 | 1.0 | 3.0 | 3.0 | 1.0 | 1.0 | 1.0 | 3.0 | 2.0 | 1.0 | 15.0 | 3.0 | 4.0 | 1.0 | 3.0 | 3.0 | 4.0 | 4.0 | 6.0 | 3.0 | 4.0 | 7.0 | 7.0 | 7.0 | 3.0 | 3.0 | 2.0 | 0.0 | 1.0 | 5.0 | 1.0 | 0.0 | 4.0 | 3.0 | 9.0 | 10.0 | 0.0 | 1.0 | 5.0 | 1992.0 | W | 2.0 | 4 | 4C | 24 | 1.0 | 3.0 | 1.0 | 0.0 | 3.0 | 2.0 | 4.0 | 4.0 | 4.0 | 2.0 | 3.0 | 2.0 | 2.0 | 712.0 | 3.0 | 3.0 | 1.0 | 0.0 | 1.0 | 4.0 | 4.0 | 3.0 | 5.0 | 2.0 |
| 3 | 4.0 | 2.0 | 2.0 | 4.0 | 2.0 | 5.0 | 2.0 | 1.0 | 2.0 | 6.0 | 1.0 | 0.0 | 2.0 | NaN | NaN | NaN | NaN | 9.0 | 4.0 | 1.0 | 8.0 | 2.0 | 5.0 | 1.0 | 2.0 | 1.0 | 4.0 | 4.0 | 7.0 | 4.0 | 3.0 | 4.0 | 4.0 | 5.0 | 4.0 | 4.0 | 1.0 | 0.0 | 1.0 | 3.0 | 0.0 | 0.0 | 1.0 | NaN | 9.0 | 1.0 | 0.0 | 1.0 | 4.0 | 1997.0 | W | 7.0 | 2 | 2A | 12 | 4.0 | 1.0 | 0.0 | 0.0 | 4.0 | 4.0 | 2.0 | 6.0 | 4.0 | NaN | 4.0 | 1.0 | NaN | 596.0 | 2.0 | 2.0 | 2.0 | 0.0 | 1.0 | 3.0 | 4.0 | 2.0 | 3.0 | 3.0 |
| 4 | 3.0 | 1.0 | 5.0 | 4.0 | 3.0 | 4.0 | 1.0 | 3.0 | 2.0 | 5.0 | 5.0 | 0.0 | 3.0 | 32.0 | 10.0 | 10.0 | 5.0 | 3.0 | 2.0 | 1.0 | 8.0 | 5.0 | 6.0 | 4.0 | 4.0 | 2.0 | 7.0 | 4.0 | 4.0 | 6.0 | 2.0 | 3.0 | 2.0 | 2.0 | 4.0 | 2.0 | 2.0 | 0.0 | 2.0 | 4.0 | 4.0 | 0.0 | 5.0 | 2.0 | 9.0 | 3.0 | 0.0 | 1.0 | 4.0 | 1992.0 | W | 3.0 | 6 | 6B | 43 | 1.0 | 4.0 | 1.0 | 0.0 | 3.0 | 2.0 | 5.0 | 1.0 | 5.0 | 3.0 | 3.0 | 5.0 | 5.0 | 435.0 | 2.0 | 4.0 | 2.0 | 1.0 | 2.0 | 3.0 | 3.0 | 4.0 | 6.0 | 5.0 |
azdias_demographics_data.shape
(891221, 79)
Discussion 1.1.2: Assess Missing Data in Each Column¶
From The histogram plot and barplot there are 6 columns that are outliers ['ALTER_HH', 'GEBURTSJAHR', 'KBA05_BAUMAX', 'KK_KUNDENTYP', 'AGER_TYP','TITEL_KZ'] These columns have more than 20% of missing data
Step 1.1.3: Assess Missing Data in Each Row¶
Now, you'll perform a similar assessment for the rows of the dataset. How much data is missing in each row? As with the columns, you should see some groups of points that have a very different numbers of missing values. Divide the data into two subsets: one for data points that are above some threshold for missing values, and a second subset for points below that threshold.
In order to know what to do with the outlier rows, we should see if the distribution of data values on columns that are not missing data (or are missing very little data) are similar or different between the two groups. Select at least five of these columns and compare the distribution of values.
- You can use seaborn's
countplot()function to create a bar chart of code frequencies and matplotlib'ssubplot()function to put bar charts for the two subplots side by side. - To reduce repeated code, you might want to write a function that can perform this comparison, taking as one of its arguments a column to be compared.
Depending on what you observe in your comparison, this will have implications on how you approach your conclusions later in the analysis. If the distributions of non-missing features look similar between the data with many missing values and the data with few or no missing values, then we could argue that simply dropping those points from the analysis won't present a major issue. On the other hand, if the data with many missing values looks very different from the data with few or no missing values, then we should make a note on those data as special. We'll revisit these data later on. Either way, you should continue your analysis for now using just the subset of the data with few or no missing values.
# How much data is missing in each row of the dataset?
nans_rows_values = azdias_demographics_data.isnull().sum(axis=1)
nans_rows_values.describe()
count 891221.000000 mean 5.649894 std 13.234687 min 0.000000 25% 0.000000 50% 0.000000 75% 3.000000 max 49.000000 dtype: float64
sns.distplot(nans_rows_values,
bins=25)
plt.xlabel('percentage of missing values for each Row')
plt.ylabel('Counts')
plt.title('Histogram for number of missing values')
plt.show()
/tmp/ipykernel_13/3930819879.py:1: UserWarning: `distplot` is a deprecated function and will be removed in seaborn v0.14.0. Please adapt your code to use either `displot` (a figure-level function with similar flexibility) or `histplot` (an axes-level function for histograms). For a guide to updating your code to use the new functions, please see https://gist.github.com/mwaskom/de44147ed2974457ad6372750bbe5751 sns.distplot(nans_rows_values,
# Write code to divide the data into two subsets based on the number of missing
# values in each row.
missing_values_below_threshold = azdias_demographics_data[nans_rows_values<20]
missing_values_above_threshold = azdias_demographics_data[nans_rows_values>=20]
nans_values = nans_values[nans_values>0]
nans_values
ALTERSKATEGORIE_GROB 0.323264 GFK_URLAUBERTYP 0.544646 LP_STATUS_GROB 0.544646 LP_STATUS_FEIN 0.544646 ONLINE_AFFINITAET 0.544646 RETOURTYP_BK_S 0.544646 CJT_GESAMTTYP 0.544646 HH_EINKOMMEN_SCORE 2.058749 WOHNDAUER_2008 8.247000 ANZ_TITEL 8.247000 SOHO_KZ 8.247000 ANZ_PERSONEN 8.247000 KONSUMNAEHE 8.299737 LP_FAMILIE_GROB 8.728699 LP_FAMILIE_FEIN 8.728699 OST_WEST_KZ 10.451729 WOHNLAGE 10.451729 GEBAEUDETYP 10.451729 MIN_GEBAEUDEJAHR 10.451729 GEBAEUDETYP_RASTER 10.452514 BALLRAUM 10.518154 EWDICHTE 10.518154 INNENSTADT 10.518154 LP_LEBENSPHASE_GROB 10.611509 ANZ_HH_TITEL 10.884842 ORTSGR_KLS9 10.914689 ARBEIT 10.926022 RELAT_AB 10.926022 LP_LEBENSPHASE_FEIN 10.954859 CAMEO_DEUG_2015 11.147852 CAMEO_DEU_2015 11.147852 CAMEO_INTL_2015 11.147852 ANZ_HAUSHALTE_AKTIV 11.176913 KBA13_ANZAHL_PKW 11.871354 PRAEGENDE_JUGENDJAHRE 12.136608 NATIONALITAET_KZ 12.153551 HEALTH_TYP 12.476816 VERS_TYP 12.476816 SHOPPER_TYP 12.476816 PLZ8_ANTG2 13.073637 PLZ8_ANTG3 13.073637 PLZ8_ANTG1 13.073637 PLZ8_ANTG4 13.073637 PLZ8_BAUMAX 13.073637 PLZ8_HHZ 13.073637 PLZ8_GBZ 13.073637 KBA05_ANTG3 14.959701 KBA05_ANTG2 14.959701 KBA05_ANTG1 14.959701 MOBI_REGIO 14.959701 KBA05_GBZ 14.959701 KBA05_ANTG4 14.959701 W_KEIT_KIND_HH 16.605084 KKK 17.735668 REGIOTYP 17.735668 ALTER_HH 34.813699 GEBURTSJAHR 44.020282 KBA05_BAUMAX 53.468668 KK_KUNDENTYP 65.596749 AGER_TYP 76.955435 TITEL_KZ 99.757636 dtype: float64
nans_columns= nans_values[nans_values<20].index
top_few_missing_cols=nans_columns[:5]
top_few_missing_cols
Index(['ALTERSKATEGORIE_GROB', 'GFK_URLAUBERTYP', 'LP_STATUS_GROB',
'LP_STATUS_FEIN', 'ONLINE_AFFINITAET'],
dtype='object')
def create_plot(column, df_below_thres, df_above_thres, max_categories=20):
# Precompute value counts for the column in both subsets
counts_below_thres = df_below_thres[column].value_counts().head(max_categories)
counts_above_thres = df_above_thres[column].value_counts().head(max_categories)
# Create figure with two subplots
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))
# Plot for missing values less than 20
sns.barplot(x=counts_below_thres.index, y=counts_below_thres.values, ax=ax1, color='skyblue')
ax1.set_title('Missing Values < 20', fontsize=10)
ax1.set_xlabel(column, fontsize=8)
ax1.set_ylabel('Count', fontsize=8)
ax1.tick_params(axis='x', rotation=45, labelsize=7)
# Plot for missing values above 20
sns.barplot(x=counts_above_thres.index, y=counts_above_thres.values, ax=ax2, color='salmon')
ax2.set_title('Missing Values >= 20', fontsize=10)
ax2.set_xlabel(column, fontsize=8)
ax2.set_ylabel('Count', fontsize=8)
ax2.tick_params(axis='x', rotation=45, labelsize=7)
# Adjust layout and show
plt.tight_layout()
plt.show()
# Precompute subsets (do this once outside the function)
# Assuming missing_less_25 and missing_above_25 are index-based filters
df_less_20 = azdias_demographics_data.loc[missing_values_below_threshold.index]
df_above_20 = azdias_demographics_data.loc[missing_values_above_threshold.index]
# Compare the distribution of values for at least five columns where there are
# no or few missing values, between the two subsets.
for i in range(5):
create_plot(top_few_missing_cols[i],df_less_20, df_above_20)
Discussion 1.1.3: Assess Missing Data in Each Row¶
Based on the histogram and the bar plot we observed that the counts of the unique values for the 5 columns we selected, is much more when the missing number in a row is less than 20 than when the missing number of rows is more than 20;
which means that the missing numbers is more when we have missing number of rows greater than 20. So we continue with the df_above_20
df_less_20 = df_less_20.fillna(df_less_20.mode().iloc[0]) # fill the missing values with the most frequent value
df_less_20.describe()
| ALTERSKATEGORIE_GROB | ANREDE_KZ | CJT_GESAMTTYP | FINANZ_MINIMALIST | FINANZ_SPARER | FINANZ_VORSORGER | FINANZ_ANLEGER | FINANZ_UNAUFFAELLIGER | FINANZ_HAUSBAUER | FINANZTYP | GFK_URLAUBERTYP | GREEN_AVANTGARDE | HEALTH_TYP | LP_LEBENSPHASE_FEIN | LP_LEBENSPHASE_GROB | LP_FAMILIE_FEIN | LP_FAMILIE_GROB | LP_STATUS_FEIN | LP_STATUS_GROB | NATIONALITAET_KZ | PRAEGENDE_JUGENDJAHRE | RETOURTYP_BK_S | SEMIO_SOZ | SEMIO_FAM | SEMIO_REL | SEMIO_MAT | SEMIO_VERT | SEMIO_LUST | SEMIO_ERL | SEMIO_KULT | SEMIO_RAT | SEMIO_KRIT | SEMIO_DOM | SEMIO_KAEM | SEMIO_PFLICHT | SEMIO_TRADV | SHOPPER_TYP | SOHO_KZ | VERS_TYP | ZABEOTYP | ANZ_PERSONEN | ANZ_TITEL | HH_EINKOMMEN_SCORE | W_KEIT_KIND_HH | WOHNDAUER_2008 | ANZ_HAUSHALTE_AKTIV | ANZ_HH_TITEL | GEBAEUDETYP | KONSUMNAEHE | MIN_GEBAEUDEJAHR | WOHNLAGE | KBA05_ANTG1 | KBA05_ANTG2 | KBA05_ANTG3 | KBA05_ANTG4 | KBA05_GBZ | BALLRAUM | EWDICHTE | INNENSTADT | GEBAEUDETYP_RASTER | KKK | MOBI_REGIO | ONLINE_AFFINITAET | REGIOTYP | KBA13_ANZAHL_PKW | PLZ8_ANTG1 | PLZ8_ANTG2 | PLZ8_ANTG3 | PLZ8_ANTG4 | PLZ8_BAUMAX | PLZ8_HHZ | PLZ8_GBZ | ARBEIT | ORTSGR_KLS9 | RELAT_AB | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.00000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 | 797077.000000 |
| mean | 2.795889 | 1.521377 | 3.505508 | 3.058727 | 2.715815 | 3.432951 | 2.840685 | 2.656884 | 3.115607 | 3.799344 | 7.497141 | 0.219722 | 2.235834 | 15.360383 | 4.774415 | 3.798516 | 2.323862 | 4.768006 | 2.451369 | 1.161456 | 9.447494 | 3.451732 | 4.14123 | 4.113152 | 3.993661 | 3.886485 | 4.277446 | 4.334015 | 4.622254 | 4.132843 | 3.893664 | 4.548362 | 4.554295 | 4.294187 | 4.184349 | 3.725147 | 1.551983 | 0.008414 | 1.540041 | 3.379054 | 1.729775 | 0.004151 | 4.415189 | 4.297359 | 7.912239 | 8.300877 | 0.040475 | 2.799820 | 3.022634 | 1993.257848 | 4.054119 | 1.420801 | 1.203356 | 0.593825 | 0.290888 | 3.150757 | 4.154264 | 3.941083 | 4.549493 | 3.738190 | 2.745691 | 2.866965 | 2.740258 | 4.595157 | 631.470423 | 2.246211 | 2.807429 | 1.606795 | 0.679538 | 1.917420 | 3.595612 | 3.370378 | 3.171405 | 5.292297 | 3.070929 |
| std | 1.016826 | 0.499543 | 1.533402 | 1.377570 | 1.485032 | 1.376959 | 1.472527 | 1.399118 | 1.407806 | 2.084767 | 3.580786 | 0.414058 | 0.756382 | 12.401118 | 3.697161 | 3.900154 | 1.687275 | 3.522233 | 1.511468 | 0.465864 | 4.049160 | 1.455128 | 1.94108 | 1.913484 | 1.910233 | 1.913611 | 1.944819 | 2.103057 | 1.826708 | 1.958003 | 1.653117 | 1.759666 | 1.826313 | 1.867894 | 1.854900 | 1.765480 | 1.009655 | 0.091344 | 0.498394 | 1.407834 | 1.156330 | 0.068529 | 1.545029 | 1.777945 | 1.920547 | 15.631775 | 0.323412 | 2.657595 | 1.550727 | 3.282927 | 1.947922 | 1.406629 | 1.244640 | 0.997401 | 0.626330 | 1.296881 | 2.183554 | 1.719255 | 2.028286 | 0.923085 | 0.942608 | 1.456553 | 1.554415 | 1.809272 | 350.634939 | 0.959177 | 0.907885 | 0.975076 | 0.726103 | 1.447445 | 0.965521 | 1.097687 | 0.998218 | 2.297430 | 1.356907 |
| min | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 0.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.00000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 0.000000 | 0.000000 | 1.000000 | 1.000000 | 0.000000 | 0.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 0.000000 | 1.000000 | 1.000000 | 1985.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 0.000000 | 1.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 |
| 25% | 2.000000 | 1.000000 | 2.000000 | 2.000000 | 1.000000 | 2.000000 | 1.000000 | 1.000000 | 2.000000 | 2.000000 | 4.000000 | 0.000000 | 2.000000 | 5.000000 | 2.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 1.000000 | 6.000000 | 2.000000 | 2.00000 | 2.000000 | 3.000000 | 2.000000 | 2.000000 | 2.000000 | 3.000000 | 3.000000 | 3.000000 | 3.000000 | 3.000000 | 3.000000 | 3.000000 | 2.000000 | 1.000000 | 0.000000 | 1.000000 | 3.000000 | 1.000000 | 0.000000 | 3.000000 | 3.000000 | 8.000000 | 1.000000 | 0.000000 | 1.000000 | 2.000000 | 1992.000000 | 3.000000 | 0.000000 | 0.000000 | 0.000000 | 0.000000 | 2.000000 | 2.000000 | 2.000000 | 3.000000 | 3.000000 | 2.000000 | 1.000000 | 1.000000 | 3.000000 | 386.000000 | 2.000000 | 2.000000 | 1.000000 | 0.000000 | 1.000000 | 3.000000 | 3.000000 | 3.000000 | 4.000000 | 2.000000 |
| 50% | 3.000000 | 2.000000 | 4.000000 | 3.000000 | 3.000000 | 4.000000 | 3.000000 | 2.000000 | 3.000000 | 4.000000 | 8.000000 | 0.000000 | 2.000000 | 12.000000 | 3.000000 | 1.000000 | 1.000000 | 4.000000 | 2.000000 | 1.000000 | 10.000000 | 4.000000 | 4.00000 | 4.000000 | 4.000000 | 4.000000 | 5.000000 | 5.000000 | 4.000000 | 4.000000 | 4.000000 | 5.000000 | 5.000000 | 4.000000 | 4.000000 | 4.000000 | 1.000000 | 0.000000 | 2.000000 | 3.000000 | 1.000000 | 0.000000 | 5.000000 | 5.000000 | 9.000000 | 4.000000 | 0.000000 | 1.000000 | 3.000000 | 1992.000000 | 3.000000 | 1.000000 | 1.000000 | 0.000000 | 0.000000 | 3.000000 | 5.000000 | 4.000000 | 5.000000 | 4.000000 | 3.000000 | 3.000000 | 3.000000 | 5.000000 | 554.000000 | 2.000000 | 3.000000 | 2.000000 | 1.000000 | 1.000000 | 3.000000 | 3.000000 | 3.000000 | 5.000000 | 3.000000 |
| 75% | 4.000000 | 2.000000 | 5.000000 | 4.000000 | 4.000000 | 5.000000 | 4.000000 | 4.000000 | 4.000000 | 6.000000 | 11.000000 | 0.000000 | 3.000000 | 27.000000 | 8.000000 | 8.000000 | 4.000000 | 9.000000 | 4.000000 | 1.000000 | 14.000000 | 5.000000 | 6.00000 | 6.000000 | 5.000000 | 5.000000 | 6.000000 | 6.000000 | 6.000000 | 6.000000 | 5.000000 | 6.000000 | 6.000000 | 6.000000 | 6.000000 | 5.000000 | 2.000000 | 0.000000 | 2.000000 | 4.000000 | 2.000000 | 0.000000 | 6.000000 | 6.000000 | 9.000000 | 9.000000 | 0.000000 | 3.000000 | 4.000000 | 1993.000000 | 5.000000 | 3.000000 | 2.000000 | 1.000000 | 0.000000 | 4.000000 | 6.000000 | 6.000000 | 6.000000 | 4.000000 | 3.000000 | 4.000000 | 4.000000 | 6.000000 | 793.000000 | 3.000000 | 3.000000 | 2.000000 | 1.000000 | 3.000000 | 4.000000 | 4.000000 | 4.000000 | 7.000000 | 4.000000 |
| max | 4.000000 | 2.000000 | 6.000000 | 5.000000 | 5.000000 | 5.000000 | 5.000000 | 5.000000 | 5.000000 | 6.000000 | 12.000000 | 1.000000 | 3.000000 | 40.000000 | 12.000000 | 11.000000 | 5.000000 | 10.000000 | 5.000000 | 3.000000 | 15.000000 | 5.000000 | 7.00000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 7.000000 | 3.000000 | 1.000000 | 2.000000 | 6.000000 | 45.000000 | 4.000000 | 6.000000 | 6.000000 | 9.000000 | 595.000000 | 23.000000 | 8.000000 | 7.000000 | 2016.000000 | 8.000000 | 4.000000 | 4.000000 | 3.000000 | 2.000000 | 5.000000 | 7.000000 | 6.000000 | 8.000000 | 5.000000 | 4.000000 | 6.000000 | 5.000000 | 7.000000 | 2300.000000 | 4.000000 | 4.000000 | 3.000000 | 2.000000 | 5.000000 | 5.000000 | 5.000000 | 5.000000 | 9.000000 | 5.000000 |
Step 1.2: Select and Re-Encode Features¶
Checking for missing data isn't the only way in which you can prepare a dataset for analysis. Since the unsupervised learning techniques to be used will only work on data that is encoded numerically, you need to make a few encoding changes or additional assumptions to be able to make progress. In addition, while almost all of the values in the dataset are encoded using numbers, not all of them represent numeric values. Check the third column of the feature summary (feat_info) for a summary of types of measurement.
- For numeric and interval data, these features can be kept without changes.
- Most of the variables in the dataset are ordinal in nature. While ordinal values may technically be non-linear in spacing, make the simplifying assumption that the ordinal variables can be treated as being interval in nature (that is, kept without any changes).
- Special handling may be necessary for the remaining two variable types: categorical, and 'mixed'.
In the first two parts of this sub-step, you will perform an investigation of the categorical and mixed-type features and make a decision on each of them, whether you will keep, drop, or re-encode each. Then, in the last part, you will create a new data frame with only the selected and engineered columns.
Data wrangling is often the trickiest part of the data analysis process, and there's a lot of it to be done here. But stick with it: once you're done with this step, you'll be ready to get to the machine learning parts of the project!
azdias_feature_summary.head()
| attribute | information_level | type | missing_or_unknown | |
|---|---|---|---|---|
| 0 | AGER_TYP | person | categorical | [-1,0] |
| 1 | ALTERSKATEGORIE_GROB | person | ordinal | [-1,0,9] |
| 2 | ANREDE_KZ | person | categorical | [-1,0] |
| 3 | CJT_GESAMTTYP | person | categorical | [0] |
| 4 | FINANZ_MINIMALIST | person | ordinal | [-1] |
# How many features are there of each data type?
feat_info = azdias_feature_summary[azdias_feature_summary['attribute'].isin(df_less_20.columns)]
feat_info['type'].value_counts()
type ordinal 49 categorical 18 mixed 6 numeric 6 Name: count, dtype: int64
Step 1.2.1: Re-Encode Categorical Features¶
For categorical data, you would ordinarily need to encode the levels as dummy variables. Depending on the number of categories, perform one of the following:
- For binary (two-level) categoricals that take numeric values, you can keep them without needing to do anything.
- There is one binary variable that takes on non-numeric values. For this one, you need to re-encode the values as numbers or create a dummy variable.
- For multi-level categoricals (three or more values), you can choose to encode the values using multiple dummy variables (e.g. via OneHotEncoder), or (to keep things straightforward) just drop them from the analysis. As always, document your choices in the Discussion section.
categorical_data = feat_info[feat_info['type'] == "categorical"]
categories_cols= categorical_data.attribute
categories_cols.iloc[0]
'ANREDE_KZ'
# Assess categorical variables: which are binary, which are multi-level, and
# which one needs to be re-encoded?
binary_categoricals = []
multi_class_categoricals = []
for column in categories_cols:
if azdias_demographics_data[column].nunique() == 2:
binary_categoricals.append(column)
else:
multi_class_categoricals.append(column)
print("Number of Binary Class Categorical Data {} VS Multi Class Categorical Data {}".format(len(binary_categoricals), len(multi_class_categoricals)))
Number of Binary Class Categorical Data 5 VS Multi Class Categorical Data 13
df_less_20[binary_categoricals].value_counts()
ANREDE_KZ GREEN_AVANTGARDE SOHO_KZ VERS_TYP OST_WEST_KZ
2.0 0.0 0.0 2.0 W 130472
1.0 0.0 0.0 2.0 W 130294
2.0 0.0 0.0 1.0 W 121795
1.0 0.0 0.0 1.0 W 84506
1.0 0.0 2.0 W 41702
1.0 W 41183
2.0 0.0 0.0 1.0 O 40887
1.0 0.0 0.0 2.0 O 39417
2.0 1.0 0.0 2.0 W 39373
0.0 0.0 2.0 O 37928
1.0 0.0 1.0 W 33968
1.0 0.0 0.0 1.0 O 31516
1.0 0.0 1.0 O 5469
2.0 1.0 0.0 1.0 O 4171
1.0 1.0 0.0 2.0 O 4153
2.0 1.0 0.0 2.0 O 3536
1.0 0.0 1.0 2.0 W 1122
2.0 0.0 1.0 2.0 W 1092
1.0 W 1020
1.0 0.0 1.0 1.0 W 742
1.0 1.0 1.0 W 383
2.0 1.0 1.0 2.0 W 364
1.0 1.0 1.0 2.0 W 359
2.0 1.0 1.0 1.0 W 320
0.0 1.0 1.0 O 309
1.0 0.0 1.0 2.0 O 288
2.0 0.0 1.0 2.0 O 286
1.0 0.0 1.0 1.0 O 268
1.0 1.0 1.0 O 53
2.0 O 44
2.0 1.0 1.0 1.0 O 33
2.0 O 24
Name: count, dtype: int64
for col in binary_categoricals:
print(df_less_20[col].value_counts(), "\n")
print("="*30)
ANREDE_KZ 2.0 415578 1.0 381499 Name: count, dtype: int64 ============================== GREEN_AVANTGARDE 0.0 621942 1.0 175135 Name: count, dtype: int64 ============================== SOHO_KZ 0.0 790370 1.0 6707 Name: count, dtype: int64 ============================== VERS_TYP 2.0 430454 1.0 366623 Name: count, dtype: int64 ============================== OST_WEST_KZ W 628695 O 168382 Name: count, dtype: int64 ==============================
OST_WEST_KZ_mappibg = {'W':0, 'O':1}
VERS_TYP_ANREDE_KZ_mapping = {1:1, 2:0}
int_mapping = {0.0:0, 1.0:1}
df_less_20['OST_WEST_KZ'] = df_less_20['OST_WEST_KZ'].map(OST_WEST_KZ_mappibg)
df_less_20['VERS_TYP'] = df_less_20['VERS_TYP'].map(VERS_TYP_ANREDE_KZ_mapping)
df_less_20['ANREDE_KZ'] = df_less_20['ANREDE_KZ'].map(VERS_TYP_ANREDE_KZ_mapping)
df_less_20['SOHO_KZ'] = df_less_20['SOHO_KZ'].map(int_mapping)
df_less_20['GREEN_AVANTGARDE'] = df_less_20['GREEN_AVANTGARDE'].map(int_mapping)
df_less_20.head()
| ALTERSKATEGORIE_GROB | ANREDE_KZ | CJT_GESAMTTYP | FINANZ_MINIMALIST | FINANZ_SPARER | FINANZ_VORSORGER | FINANZ_ANLEGER | FINANZ_UNAUFFAELLIGER | FINANZ_HAUSBAUER | FINANZTYP | GFK_URLAUBERTYP | GREEN_AVANTGARDE | HEALTH_TYP | LP_LEBENSPHASE_FEIN | LP_LEBENSPHASE_GROB | LP_FAMILIE_FEIN | LP_FAMILIE_GROB | LP_STATUS_FEIN | LP_STATUS_GROB | NATIONALITAET_KZ | PRAEGENDE_JUGENDJAHRE | RETOURTYP_BK_S | SEMIO_SOZ | SEMIO_FAM | SEMIO_REL | SEMIO_MAT | SEMIO_VERT | SEMIO_LUST | SEMIO_ERL | SEMIO_KULT | SEMIO_RAT | SEMIO_KRIT | SEMIO_DOM | SEMIO_KAEM | SEMIO_PFLICHT | SEMIO_TRADV | SHOPPER_TYP | SOHO_KZ | VERS_TYP | ZABEOTYP | ANZ_PERSONEN | ANZ_TITEL | HH_EINKOMMEN_SCORE | W_KEIT_KIND_HH | WOHNDAUER_2008 | ANZ_HAUSHALTE_AKTIV | ANZ_HH_TITEL | GEBAEUDETYP | KONSUMNAEHE | MIN_GEBAEUDEJAHR | OST_WEST_KZ | WOHNLAGE | CAMEO_DEUG_2015 | CAMEO_DEU_2015 | CAMEO_INTL_2015 | KBA05_ANTG1 | KBA05_ANTG2 | KBA05_ANTG3 | KBA05_ANTG4 | KBA05_GBZ | BALLRAUM | EWDICHTE | INNENSTADT | GEBAEUDETYP_RASTER | KKK | MOBI_REGIO | ONLINE_AFFINITAET | REGIOTYP | KBA13_ANZAHL_PKW | PLZ8_ANTG1 | PLZ8_ANTG2 | PLZ8_ANTG3 | PLZ8_ANTG4 | PLZ8_BAUMAX | PLZ8_HHZ | PLZ8_GBZ | ARBEIT | ORTSGR_KLS9 | RELAT_AB | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 1.0 | 0 | 5.0 | 1.0 | 5.0 | 2.0 | 5.0 | 4.0 | 5.0 | 1.0 | 10.0 | 0 | 3.0 | 21.0 | 6.0 | 5.0 | 3.0 | 2.0 | 1.0 | 1.0 | 14.0 | 1.0 | 5.0 | 4.0 | 4.0 | 3.0 | 1.0 | 2.0 | 2.0 | 3.0 | 6.0 | 4.0 | 7.0 | 4.0 | 7.0 | 6.0 | 3.0 | 1 | 0 | 5.0 | 2.0 | 0.0 | 6.0 | 3.0 | 9.0 | 11.0 | 0.0 | 8.0 | 1.0 | 1992.0 | 0 | 4.0 | 8 | 8A | 51 | 0.0 | 0.0 | 0.0 | 2.0 | 1.0 | 6.0 | 3.0 | 8.0 | 3.0 | 2.0 | 1.0 | 3.0 | 3.0 | 963.0 | 2.0 | 3.0 | 2.0 | 1.0 | 1.0 | 5.0 | 4.0 | 3.0 | 5.0 | 4.0 |
| 2 | 3.0 | 0 | 3.0 | 1.0 | 4.0 | 1.0 | 2.0 | 3.0 | 5.0 | 1.0 | 10.0 | 1 | 3.0 | 3.0 | 1.0 | 1.0 | 1.0 | 3.0 | 2.0 | 1.0 | 15.0 | 3.0 | 4.0 | 1.0 | 3.0 | 3.0 | 4.0 | 4.0 | 6.0 | 3.0 | 4.0 | 7.0 | 7.0 | 7.0 | 3.0 | 3.0 | 2.0 | 0 | 1 | 5.0 | 1.0 | 0.0 | 4.0 | 3.0 | 9.0 | 10.0 | 0.0 | 1.0 | 5.0 | 1992.0 | 0 | 2.0 | 4 | 4C | 24 | 1.0 | 3.0 | 1.0 | 0.0 | 3.0 | 2.0 | 4.0 | 4.0 | 4.0 | 2.0 | 3.0 | 2.0 | 2.0 | 712.0 | 3.0 | 3.0 | 1.0 | 0.0 | 1.0 | 4.0 | 4.0 | 3.0 | 5.0 | 2.0 |
| 3 | 4.0 | 0 | 2.0 | 4.0 | 2.0 | 5.0 | 2.0 | 1.0 | 2.0 | 6.0 | 1.0 | 0 | 2.0 | 1.0 | 2.0 | 1.0 | 1.0 | 9.0 | 4.0 | 1.0 | 8.0 | 2.0 | 5.0 | 1.0 | 2.0 | 1.0 | 4.0 | 4.0 | 7.0 | 4.0 | 3.0 | 4.0 | 4.0 | 5.0 | 4.0 | 4.0 | 1.0 | 0 | 1 | 3.0 | 0.0 | 0.0 | 1.0 | 6.0 | 9.0 | 1.0 | 0.0 | 1.0 | 4.0 | 1997.0 | 0 | 7.0 | 2 | 2A | 12 | 4.0 | 1.0 | 0.0 | 0.0 | 4.0 | 4.0 | 2.0 | 6.0 | 4.0 | 3.0 | 4.0 | 1.0 | 6.0 | 596.0 | 2.0 | 2.0 | 2.0 | 0.0 | 1.0 | 3.0 | 4.0 | 2.0 | 3.0 | 3.0 |
| 4 | 3.0 | 1 | 5.0 | 4.0 | 3.0 | 4.0 | 1.0 | 3.0 | 2.0 | 5.0 | 5.0 | 0 | 3.0 | 32.0 | 10.0 | 10.0 | 5.0 | 3.0 | 2.0 | 1.0 | 8.0 | 5.0 | 6.0 | 4.0 | 4.0 | 2.0 | 7.0 | 4.0 | 4.0 | 6.0 | 2.0 | 3.0 | 2.0 | 2.0 | 4.0 | 2.0 | 2.0 | 0 | 0 | 4.0 | 4.0 | 0.0 | 5.0 | 2.0 | 9.0 | 3.0 | 0.0 | 1.0 | 4.0 | 1992.0 | 0 | 3.0 | 6 | 6B | 43 | 1.0 | 4.0 | 1.0 | 0.0 | 3.0 | 2.0 | 5.0 | 1.0 | 5.0 | 3.0 | 3.0 | 5.0 | 5.0 | 435.0 | 2.0 | 4.0 | 2.0 | 1.0 | 2.0 | 3.0 | 3.0 | 4.0 | 6.0 | 5.0 |
| 5 | 1.0 | 0 | 2.0 | 3.0 | 1.0 | 5.0 | 2.0 | 2.0 | 5.0 | 2.0 | 1.0 | 0 | 3.0 | 8.0 | 2.0 | 1.0 | 1.0 | 4.0 | 2.0 | 1.0 | 3.0 | 3.0 | 2.0 | 4.0 | 7.0 | 4.0 | 2.0 | 2.0 | 2.0 | 5.0 | 7.0 | 4.0 | 4.0 | 4.0 | 7.0 | 6.0 | 0.0 | 0 | 0 | 4.0 | 1.0 | 0.0 | 5.0 | 6.0 | 9.0 | 5.0 | 0.0 | 1.0 | 5.0 | 1992.0 | 0 | 7.0 | 8 | 8C | 54 | 2.0 | 2.0 | 0.0 | 0.0 | 4.0 | 6.0 | 2.0 | 7.0 | 4.0 | 4.0 | 4.0 | 1.0 | 5.0 | 1300.0 | 2.0 | 3.0 | 1.0 | 1.0 | 1.0 | 5.0 | 5.0 | 2.0 | 3.0 | 3.0 |
for col in multi_class_categoricals:
print(df_less_20[col].value_counts(), "\n")
print("="*30)
CJT_GESAMTTYP 4.0 202721 3.0 147068 2.0 141166 5.0 111032 6.0 101898 1.0 93192 Name: count, dtype: int64 ============================== FINANZTYP 6.0 289004 1.0 196805 5.0 106220 2.0 104577 4.0 55874 3.0 44597 Name: count, dtype: int64 ============================== GFK_URLAUBERTYP 12.0 134615 10.0 102748 8.0 82992 11.0 75051 5.0 70468 4.0 60413 9.0 57046 3.0 53094 1.0 50640 2.0 43647 7.0 40642 6.0 25721 Name: count, dtype: int64 ============================== LP_FAMILIE_FEIN 1.0 433230 10.0 128902 2.0 98491 11.0 48727 8.0 21777 7.0 19568 4.0 11573 5.0 11164 9.0 10451 6.0 8512 3.0 4682 Name: count, dtype: int64 ============================== LP_FAMILIE_GROB 1.0 433230 5.0 188080 2.0 98491 4.0 49857 3.0 27419 Name: count, dtype: int64 ============================== LP_STATUS_FEIN 1.0 211398 9.0 136229 10.0 111538 2.0 111016 4.0 73938 3.0 68893 6.0 28870 5.0 27472 8.0 18525 7.0 9198 Name: count, dtype: int64 ============================== LP_STATUS_GROB 1.0 322414 2.0 170303 4.0 154754 5.0 111538 3.0 38068 Name: count, dtype: int64 ============================== NATIONALITAET_KZ 1.0 700921 2.0 63619 3.0 32537 Name: count, dtype: int64 ============================== SHOPPER_TYP 1.0 283490 2.0 205874 3.0 180604 0.0 127109 Name: count, dtype: int64 ============================== ZABEOTYP 3.0 281772 4.0 207383 1.0 123270 5.0 80892 6.0 70817 2.0 32943 Name: count, dtype: int64 ============================== GEBAEUDETYP 1.0 459844 3.0 178507 8.0 152439 2.0 4789 4.0 885 6.0 612 5.0 1 Name: count, dtype: int64 ============================== CAMEO_DEUG_2015 8 140261 9 108138 6 105819 4 103814 3 86612 2 83149 7 77888 5 55216 1 36180 Name: count, dtype: int64 ============================== CAMEO_DEU_2015 6B 62509 8A 52427 4C 47765 2D 35047 3C 34740 7A 34384 3D 34275 8B 33424 4A 33128 8C 30978 9D 28591 9B 27661 9C 24986 7B 24489 9A 20537 2C 19408 8D 17565 6E 16104 2B 15468 5D 14934 6C 14815 2A 13226 5A 12153 1D 11908 1A 10837 3A 10454 5B 10345 5C 9926 7C 9059 4B 9038 4D 8565 3B 7143 6A 6799 9E 6363 6D 6068 6F 5391 7D 5329 4E 5318 1E 5057 7E 4627 1C 4310 5F 4281 1B 4068 5E 3577 Name: count, dtype: int64 ==============================
# Re-encode categorical variable(s) to be kept in the analysis.
# df_less_25=pd.get_dummies(data=df_less_25,columns=multi_class_categoricals, drop_first=True)
print(df_less_20.shape)
(797077, 79)
Discussion 1.2.1: Re-Encode Categorical Features¶
Used pandas get_dummies function to encode multi class categorical variables, and for the binary variables (numerical and non-numerical variables) used a manual map dictionary
Step 1.2.2: Engineer Mixed-Type Features¶
There are a handful of features that are marked as "mixed" in the feature summary that require special treatment in order to be included in the analysis. There are two in particular that deserve attention; the handling of the rest are up to your own choices:
- "PRAEGENDE_JUGENDJAHRE" combines information on three dimensions: generation by decade, movement (mainstream vs. avantgarde), and nation (east vs. west). While there aren't enough levels to disentangle east from west, you should create two new variables to capture the other two dimensions: an interval-type variable for decade, and a binary variable for movement.
- "CAMEO_INTL_2015" combines information on two axes: wealth and life stage. Break up the two-digit codes by their 'tens'-place and 'ones'-place digits into two new ordinal variables (which, for the purposes of this project, is equivalent to just treating them as their raw numeric values).
- If you decide to keep or engineer new features around the other mixed-type features, make sure you note your steps in the Discussion section.
Be sure to check Data_Dictionary.md for the details needed to finish these tasks.
- PRAEGENDE_JUGENDJAHRE
- Dominating movement of person's youth (avantgarde vs. mainstream; east vs. west)
- -1: unknown
- 0: unknown
- 1: 40s - war years (Mainstream, E+W)
- 2: 40s - reconstruction years (Avantgarde, E+W)
- 3: 50s - economic miracle (Mainstream, E+W)
- 4: 50s - milk bar / Individualisation (Avantgarde, E+W)
- 5: 60s - economic miracle (Mainstream, E+W)
- 6: 60s - generation 68 / student protestors (Avantgarde, W)
- 7: 60s - opponents to the building of the Wall (Avantgarde, E)
- 8: 70s - family orientation (Mainstream, E+W)
- 9: 70s - peace movement (Avantgarde, E+W)
- 10: 80s - Generation Golf (Mainstream, W)
- 11: 80s - ecological awareness (Avantgarde, W)
- 12: 80s - FDJ / communist party youth organisation (Mainstream, E)
- 13: 80s - Swords into ploughshares (Avantgarde, E)
- 14: 90s - digital media kids (Mainstream, E+W)
- 15: 90s - ecological awareness (Avantgarde, E+W)
- Dominating movement of person's youth (avantgarde vs. mainstream; east vs. west)
# Investigate "PRAEGENDE_JUGENDJAHRE" and engineer two new variables.
mapping_decades = {
1:1,2:1,3:2,4:2,5:3,
6:3,7:3,8:4,9:4,10:5,
11:5,12:5,13:5,14:6,15:6
}
mapping_movement={
1:1,2:0,3:1,4:0,5:1,
6:0,7:0,8:1,9:0,10:1,
11:0,12:1,13:0,14:1,15:0
}
df_less_20['DECADE'] = df_less_20['PRAEGENDE_JUGENDJAHRE']
df_less_20['MOVEMENT'] = df_less_20['PRAEGENDE_JUGENDJAHRE']
df_less_20['DECADE'] = df_less_20['DECADE'].map(mapping_decades)
df_less_20['MOVEMENT'] = df_less_20['MOVEMENT'].map(mapping_movement)
df_less_20.drop(columns='PRAEGENDE_JUGENDJAHRE', axis=1, inplace=True)
- CAMEO_INTL_2015
- German CAMEO: Wealth / Life Stage Typology, mapped to international code
- -1: unknown
- 11: Wealthy Households - Pre-Family Couples & Singles
- 12: Wealthy Households - Young Couples With Children
- 13: Wealthy Households - Families With School Age Children
- 14: Wealthy Households - Older Families & Mature Couples
- 15: Wealthy Households - Elders In Retirement
- 21: Prosperous Households - Pre-Family Couples & Singles
- 22: Prosperous Households - Young Couples With Children
- 23: Prosperous Households - Families With School Age Children
- 24: Prosperous Households - Older Families & Mature Couples
- 25: Prosperous Households - Elders In Retirement
- 31: Comfortable Households - Pre-Family Couples & Singles
- 32: Comfortable Households - Young Couples With Children
- 33: Comfortable Households - Families With School Age Children
- 34: Comfortable Households - Older Families & Mature Couples
- 35: Comfortable Households - Elders In Retirement
- 41: Less Affluent Households - Pre-Family Couples & Singles
- 42: Less Affluent Households - Young Couples With Children
- 43: Less Affluent Households - Families With School Age Children
- 44: Less Affluent Households - Older Families & Mature Couples
- 45: Less Affluent Households - Elders In Retirement
- 51: Poorer Households - Pre-Family Couples & Singles
- 52: Poorer Households - Young Couples With Children
- 53: Poorer Households - Families With School Age Children
- 54: Poorer Households - Older Families & Mature Couples
- 55: Poorer Households - Elders In Retirement
- XX: unknown
- German CAMEO: Wealth / Life Stage Typology, mapped to international code
type(df_less_20['CAMEO_INTL_2015'].iloc[0])
str
df_less_20['CAMEO_INTL_2015'].info()
<class 'pandas.core.series.Series'> Index: 797077 entries, 1 to 891220 Series name: CAMEO_INTL_2015 Non-Null Count Dtype -------------- ----- 797077 non-null object dtypes: object(1) memory usage: 12.2+ MB
# Investigate "CAMEO_INTL_2015" and engineer two new variables.
wealth_decades = {
'11':1,'12':1,'13':1,'14':1,'15':1,
'21':2,'22':2,'23':2,'24':2,'25':2,
'31':3,'32':3,'33':3,'34':3,'35':3,
'41':4,'42':4,'43':4,'44':4,'45':4,
'51':5,'52':5,'53':5,'54':5,'55':5
}
life_stage_typology_movement = {
'11':1,'12':2,'13':3,'14':4,'15':5,
'21':1,'22':2,'23':3,'24':4,'25':5,
'31':1,'32':2,'33':4,'34':4,'35':5,
'41':1,'42':2,'43':3,'44':4,'45':5,
'51':1,'52':2,'53':3,'54':4,'55':5
}
df_less_20['WEALTH'] = df_less_20['CAMEO_INTL_2015']
df_less_20['LIFE_STAGE_TYPOLOGY'] = df_less_20['CAMEO_INTL_2015']
df_less_20['WEALTH'] = df_less_20['WEALTH'].map(wealth_decades)
df_less_20['LIFE_STAGE_TYPOLOGY'] = df_less_20['LIFE_STAGE_TYPOLOGY'].map(life_stage_typology_movement)
df_less_20.drop(columns='CAMEO_INTL_2015', axis=1, inplace=True)
Discussion 1.2.2: Engineer Mixed-Type Features¶
Manually encoded these two features ['CAMEO_INTL_2015', 'LIFE_STAGE_TYPOLOGY'] and droped all other mixed features
Step 1.2.3: Complete Feature Selection¶
In order to finish this step up, you need to make sure that your data frame now only has the columns that you want to keep. To summarize, the dataframe should consist of the following:
- All numeric, interval, and ordinal type columns from the original dataset.
- Binary categorical features (all numerically-encoded).
- Engineered features from other multi-level categorical features and mixed features.
Make sure that for any new columns that you have engineered, that you've excluded the original columns from the final dataset. Otherwise, their values will interfere with the analysis later on the project. For example, you should not keep "PRAEGENDE_JUGENDJAHRE", since its values won't be useful for the algorithm: only the values derived from it in the engineered features you created should be retained. As a reminder, your data should only be from the subset with few or no missing values.
# If there are other re-engineering tasks you need to perform, make sure you
# take care of them here. (Dealing with missing data will come in step 2.1.)
df_less_20.isnull().sum().sum()
0
mixed_cols = azdias_feature_summary[azdias_feature_summary.type == 'mixed'].attribute
# Do whatever you need to in order to ensure that the dataframe only contains
# the columns that should be passed to the algorithm functions.
for col in mixed_cols:
if col in df_less_20.columns:
df_less_20.drop(columns=col, axis=1, inplace=True)
df_less_20.shape
(797077, 77)
Step 1.3: Create a Cleaning Function¶
Even though you've finished cleaning up the general population demographics data, it's important to look ahead to the future and realize that you'll need to perform the same cleaning steps on the customer demographics data. In this substep, complete the function below to execute the main feature selection, encoding, and re-engineering steps you performed above. Then, when it comes to looking at the customer data in Step 3, you can just run this function on that DataFrame to get the trimmed dataset in a single step.
def binary_vs_multiclass_features(df, categories_cols):
binary_classes = []
multi_class = []
for column in categories_cols:
if df[column].nunique() == 2:
binary_classes.append(column)
else:
multi_class.append(column)
return binary_classes, multi_class
def clean_data(df, df_feat_info):
"""
Perform feature trimming, re-encoding, and engineering for demographics
data
INPUT: Demographics DataFrame
OUTPUT: Trimmed and cleaned demographics DataFrame
"""
# Put in code here to execute all main cleaning steps:
# convert missing value codes into NaNs, ...
missing_values = df_feat_info['missing_or_unknown'].apply(convert_str_to_list)
for attribute, missing_vals in zip(df_feat_info['attribute'], missing_values):
if len(missing_vals)>0:
for missing_value in missing_vals:
df.loc[df[attribute] == missing_value, attribute] = np.nan
# remove selected columns and rows, ...
# missing_values_cols = (df.isnull().sum() / df.shape[0])*100
# droping_columns = missing_values_cols[missing_values_cols>20].index
droping_columns=['ALTER_HH', 'GEBURTSJAHR', 'KBA05_BAUMAX', 'KK_KUNDENTYP', 'AGER_TYP','TITEL_KZ']
df.drop(columns=droping_columns, axis=1, inplace=True)
missing_rows_values = df.isnull().sum(axis=1)
missing_values_below_threshold = df[missing_rows_values<25]
df_new = df.loc[missing_values_below_threshold.index]
df_new = df_new.fillna(df_new.mode().iloc[0])
# select, re-encode, and engineer column values.
df_feat_info = df_feat_info[df_feat_info.attribute.isin(df_new.columns)]
categorical_data = df_feat_info[df_feat_info['type'] == "categorical"].attribute
binary, multi_class = binary_vs_multiclass_features(df, categorical_data)
OST_WEST_KZ_mappibg = {'W':0, 'O':1}
VERS_TYP_ANREDE_KZ_mapping = {1:1, 2:0}
int_mapping = {0.0:0, 1.0:1}
df_new['VERS_TYP'] = df_new['VERS_TYP'].map(VERS_TYP_ANREDE_KZ_mapping)
df_new['ANREDE_KZ'] = df_new['ANREDE_KZ'].map(VERS_TYP_ANREDE_KZ_mapping)
df_new['SOHO_KZ'] = df_new['SOHO_KZ'].map(int_mapping)
df_new['GREEN_AVANTGARDE'] = df_new['GREEN_AVANTGARDE'].map(int_mapping)
df_new['OST_WEST_KZ'] = df_new['OST_WEST_KZ'].map(OST_WEST_KZ_mappibg)
# df_new = pd.get_dummies(data=df_new, columns=multi_class, drop_first=True)
mapping_decades = {
1:1,2:1,3:2,4:2,5:3,
6:3,7:3,8:4,9:4,10:5,
11:5,12:5,13:5,14:6,15:6
}
mapping_movement={
1:1,2:0,3:1,4:0,5:1,
6:0,7:0,8:1,9:0,10:1,
11:0,12:1,13:0,14:1,15:0
}
df_new['DECADE'] = df_new['PRAEGENDE_JUGENDJAHRE']
df_new['MOVEMENT'] = df_new['PRAEGENDE_JUGENDJAHRE']
df_new['DECADE'] = df_new['DECADE'].map(mapping_decades)
df_new['MOVEMENT'] = df_new['MOVEMENT'].map(mapping_movement)
df_new.drop(columns='PRAEGENDE_JUGENDJAHRE', axis=1, inplace=True)
wealth_decades = {
'11':1,'12':1,'13':1,'14':1,'15':1,
'21':2,'22':2,'23':2,'24':2,'25':2,
'31':3,'32':3,'33':3,'34':3,'35':3,
'41':4,'42':4,'43':4,'44':4,'45':4,
'51':5,'52':5,'53':5,'54':5,'55':5
}
life_stage_typology_movement = {
'11':1,'12':2,'13':3,'14':4,'15':5,
'21':1,'22':2,'23':3,'24':4,'25':5,
'31':1,'32':2,'33':4,'34':4,'35':5,
'41':1,'42':2,'43':3,'44':4,'45':5,
'51':1,'52':2,'53':3,'54':4,'55':5
}
df_new['WEALTH'] = df_new['CAMEO_INTL_2015']
df_new['LIFE_STAGE_TYPOLOGY'] = df_new['CAMEO_INTL_2015']
df_new['WEALTH'] = df_new['WEALTH'].map(wealth_decades)
df_new['LIFE_STAGE_TYPOLOGY'] = df_new['LIFE_STAGE_TYPOLOGY'].map(life_stage_typology_movement)
df_new.drop(columns='CAMEO_INTL_2015', axis=1, inplace=True)
mixed_cols = df_feat_info[df_feat_info.type == 'mixed'].attribute
for col in mixed_cols:
if col in df_new.columns:
df_new.drop(columns=col, axis=1, inplace=True)
# Return the cleaned dataframe.
return df_new
test_df = pd.read_csv("Udacity_AZDIAS_Subset.csv",sep=';')
azdias_feature_summary = pd.read_csv("AZDIAS_Feature_Summary.csv", sep=';')
test_df = clean_data(test_df, azdias_feature_summary)
test_df.shape
(797906, 77)
Step 2: Feature Transformation¶
Step 2.1: Apply Feature Scaling¶
Before we apply dimensionality reduction techniques to the data, we need to perform feature scaling so that the principal component vectors are not influenced by the natural differences in scale for features. Starting from this part of the project, you'll want to keep an eye on the API reference page for sklearn to help you navigate to all of the classes and functions that you'll need. In this substep, you'll need to check the following:
- sklearn requires that data not have missing values in order for its estimators to work properly. So, before applying the scaler to your data, make sure that you've cleaned the DataFrame of the remaining missing values. This can be as simple as just removing all data points with missing data, or applying an SimpleImputer to replace all missing values. You might also try a more complicated procedure where you temporarily remove missing values in order to compute the scaling parameters before re-introducing those missing values and applying imputation. Think about how much missing data you have and what possible effects each approach might have on your analysis, and justify your decision in the discussion section below.
- For the actual scaling function, a StandardScaler instance is suggested, scaling each feature to mean 0 and standard deviation 1.
- For these classes, you can make use of the
.fit_transform()method to both fit a procedure to the data as well as apply the transformation to the data at the same time. Don't forget to keep the fit sklearn objects handy, since you'll be applying them to the customer demographics data towards the end of the project.
# If you've not yet cleaned the dataset of all NaN values, then investigate and
# do that now.
df_less_20.isnull().sum().sum()
0
df_less_20 = df_less_20.select_dtypes(["int", "float"])
print(df_less_20.isna().sum().sum(), np.isinf(df_less_20).sum().sum())
0 0
sc = StandardScaler()
features_sc = sc.fit_transform(df_less_20) #This likely exceeded your system's available RAM, causing the kernel to crash or disconnect.
# # Apply feature scaling to the general population demographics data.
# Another way to avoid High memory Consumption
# sc = StandardScaler()
# chunk_size = 10000
# for start in range(0, df_less_25.shape[0], chunk_size):
# chunk = df_less_25[start:start + chunk_size].astype(np.float32)
# sc.partial_fit(chunk)
# features_sc = np.vstack([sc.transform(df_less_25[start:start + chunk_size])
# for start in range(0, df_less_25.shape[0], chunk_size)])
df_sc = pd.DataFrame(data=features_sc, columns=df_less_20.columns.tolist())
df_sc.head()
| ALTERSKATEGORIE_GROB | ANREDE_KZ | CJT_GESAMTTYP | FINANZ_MINIMALIST | FINANZ_SPARER | FINANZ_VORSORGER | FINANZ_ANLEGER | FINANZ_UNAUFFAELLIGER | FINANZ_HAUSBAUER | FINANZTYP | GFK_URLAUBERTYP | GREEN_AVANTGARDE | HEALTH_TYP | LP_FAMILIE_FEIN | LP_FAMILIE_GROB | LP_STATUS_FEIN | LP_STATUS_GROB | NATIONALITAET_KZ | RETOURTYP_BK_S | SEMIO_SOZ | SEMIO_FAM | SEMIO_REL | SEMIO_MAT | SEMIO_VERT | SEMIO_LUST | SEMIO_ERL | SEMIO_KULT | SEMIO_RAT | SEMIO_KRIT | SEMIO_DOM | SEMIO_KAEM | SEMIO_PFLICHT | SEMIO_TRADV | SHOPPER_TYP | SOHO_KZ | VERS_TYP | ZABEOTYP | ANZ_PERSONEN | ANZ_TITEL | HH_EINKOMMEN_SCORE | W_KEIT_KIND_HH | WOHNDAUER_2008 | ANZ_HAUSHALTE_AKTIV | ANZ_HH_TITEL | GEBAEUDETYP | KONSUMNAEHE | MIN_GEBAEUDEJAHR | OST_WEST_KZ | KBA05_ANTG1 | KBA05_ANTG2 | KBA05_ANTG3 | KBA05_ANTG4 | KBA05_GBZ | BALLRAUM | EWDICHTE | INNENSTADT | GEBAEUDETYP_RASTER | KKK | MOBI_REGIO | ONLINE_AFFINITAET | REGIOTYP | KBA13_ANZAHL_PKW | PLZ8_ANTG1 | PLZ8_ANTG2 | PLZ8_ANTG3 | PLZ8_ANTG4 | PLZ8_HHZ | PLZ8_GBZ | ARBEIT | ORTSGR_KLS9 | RELAT_AB | DECADE | MOVEMENT | WEALTH | LIFE_STAGE_TYPOLOGY | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | -1.766173 | -0.958121 | 0.974626 | -1.494463 | 1.538139 | -1.040664 | 1.466401 | 0.959974 | 1.338532 | -1.342762 | 0.698969 | -0.530654 | 1.010291 | 0.308061 | 0.400728 | -0.785867 | -0.960239 | -0.346574 | -1.684892 | 0.442419 | -0.059134 | 0.003319 | -0.463253 | -1.685220 | -1.109821 | -1.435509 | -0.578571 | 1.274161 | -0.311629 | 1.339150 | -0.157497 | 1.517954 | 1.288519 | 1.434171 | 10.855531 | -0.922883 | 1.151377 | 0.233692 | -0.060579 | 1.025749 | -0.729696 | 0.566381 | 0.172669 | -0.125151 | 1.956725 | -1.304315 | -0.383149 | -0.517521 | -1.010076 | -0.966830 | -0.595372 | 2.728773 | -1.658408 | 0.845291 | -0.547379 | 1.701195 | -0.799699 | -0.791093 | -1.281770 | 0.167099 | -0.881658 | 0.945513 | -0.256690 | 0.212110 | 0.403256 | 0.441346 | 1.454540 | 0.573590 | -0.171711 | -0.127228 | 0.684698 | 1.098802 | 0.530654 | 1.175857 | -1.253272 |
| 1 | 0.200733 | -0.958121 | -0.329665 | -1.494463 | 0.864753 | -1.766903 | -0.570914 | 0.245238 | 1.338532 | -1.342762 | 0.698969 | 1.884467 | 1.010291 | -0.717540 | -0.784616 | -0.501956 | -0.298630 | -0.346574 | -0.310441 | -0.072758 | -1.626956 | -0.520178 | -0.463253 | -0.142659 | -0.158824 | 0.754224 | -0.578571 | 0.064325 | 1.393241 | 1.339150 | 1.448591 | -0.638498 | -0.410737 | 0.443733 | -0.092119 | 1.083561 | 1.151377 | -0.631113 | -0.060579 | -0.268726 | -0.729696 | 0.566381 | 0.108697 | -0.125151 | -0.677237 | 1.275123 | -0.383149 | -0.517521 | -0.299156 | 1.443506 | 0.407234 | -0.464432 | -0.116246 | -0.986587 | 0.034269 | -0.270915 | 0.283625 | -0.791093 | 0.091335 | -0.476230 | -1.434366 | 0.229668 | 0.785871 | 0.212110 | -0.622305 | -0.935871 | 0.418829 | 0.573590 | -0.171711 | -0.127228 | -0.789243 | 1.098802 | -1.884467 | -0.869590 | 0.755631 |
| 2 | 1.184186 | -0.958121 | -0.981810 | 0.683285 | -0.482020 | 1.138052 | -0.570914 | -1.184235 | -0.792444 | 1.055589 | -1.814447 | -0.530654 | -0.311793 | -0.717540 | -0.784616 | 1.201510 | 1.024588 | -0.346574 | -0.997667 | 0.442419 | -1.626956 | -1.043675 | -1.508398 | -0.142659 | -0.158824 | 1.301657 | -0.067846 | -0.540594 | -0.311629 | -0.303505 | 0.377866 | -0.099385 | 0.155682 | -0.546705 | -0.092119 | 1.083561 | -0.269246 | -1.495919 | -0.060579 | -2.210438 | 0.957646 | 0.566381 | -0.467054 | -0.125151 | -0.677237 | 0.630264 | 1.139883 | -0.517521 | 1.833604 | -0.163385 | -0.595372 | -0.464432 | 0.654835 | -0.070648 | -1.129027 | 0.715140 | 0.283625 | 0.269793 | 0.777888 | -1.119559 | 0.776469 | -0.101161 | -0.256690 | -0.889352 | 0.403256 | -0.935871 | -0.616882 | 0.573590 | -1.173497 | -0.997766 | -0.052273 | -0.267171 | 0.530654 | -1.551406 | -0.583638 |
| 3 | 0.200733 | 1.043709 | 0.974626 | 0.683285 | 0.191366 | 0.411813 | -1.250019 | 0.245238 | -0.792444 | 0.575919 | -0.697373 | -0.530654 | 1.010291 | 1.590062 | 1.586072 | -0.501956 | -0.298630 | -0.346574 | 1.064009 | 0.957596 | -0.059134 | 0.003319 | -0.985825 | 1.399902 | -0.158824 | -0.340642 | 0.953604 | -1.145512 | -0.879919 | -1.398609 | -1.228222 | -0.099385 | -0.977156 | 0.443733 | -0.092119 | -0.922883 | 0.441065 | 1.963302 | -0.060579 | 0.378511 | -1.292143 | 0.566381 | -0.339109 | -0.125151 | -0.677237 | 0.630264 | -0.383149 | -0.517521 | -0.299156 | 2.246951 | 0.407234 | -0.464432 | -0.116246 | -0.986587 | 0.615916 | -1.749998 | 1.366950 | 0.269793 | 0.091335 | 1.453757 | 0.223760 | -0.560328 | -0.256690 | 1.313572 | 0.403256 | 0.441346 | -0.616882 | -0.337417 | 0.830075 | 0.308041 | 1.421668 | -0.267171 | 0.530654 | 0.494041 | 0.085996 |
| 4 | -1.766173 | -0.958121 | -0.981810 | -0.042631 | -1.155407 | 1.138052 | -0.570914 | -0.469499 | 1.338532 | -0.863092 | -1.814447 | -0.530654 | 1.010291 | -0.717540 | -0.784616 | -0.218045 | -0.298630 | -0.346574 | -0.310441 | -1.103113 | -0.059134 | 1.573809 | 0.059320 | -1.171033 | -1.109821 | -1.435509 | 0.442879 | 1.879080 | -0.311629 | -0.303505 | -0.157497 | 1.517954 | 1.288519 | -1.537143 | -0.092119 | -0.922883 | 0.441065 | -0.631113 | -0.060579 | 0.378511 | 0.957646 | 0.566381 | -0.211165 | -0.125151 | -0.677237 | 1.275123 | -0.383149 | -0.517521 | 0.411764 | 0.640060 | -0.595372 | -0.464432 | 0.654835 | 0.845291 | -1.129027 | 1.208168 | 0.283625 | 1.330680 | 0.777888 | -1.119559 | 0.223760 | 1.906627 | -0.256690 | 0.212110 | -0.622305 | 0.441346 | 1.454540 | 1.484596 | -1.173497 | -0.997766 | -0.052273 | -1.633144 | 0.530654 | 1.175857 | 0.755631 |
df_sc.describe()
| ALTERSKATEGORIE_GROB | ANREDE_KZ | CJT_GESAMTTYP | FINANZ_MINIMALIST | FINANZ_SPARER | FINANZ_VORSORGER | FINANZ_ANLEGER | FINANZ_UNAUFFAELLIGER | FINANZ_HAUSBAUER | FINANZTYP | GFK_URLAUBERTYP | GREEN_AVANTGARDE | HEALTH_TYP | LP_FAMILIE_FEIN | LP_FAMILIE_GROB | LP_STATUS_FEIN | LP_STATUS_GROB | NATIONALITAET_KZ | RETOURTYP_BK_S | SEMIO_SOZ | SEMIO_FAM | SEMIO_REL | SEMIO_MAT | SEMIO_VERT | SEMIO_LUST | SEMIO_ERL | SEMIO_KULT | SEMIO_RAT | SEMIO_KRIT | SEMIO_DOM | SEMIO_KAEM | SEMIO_PFLICHT | SEMIO_TRADV | SHOPPER_TYP | SOHO_KZ | VERS_TYP | ZABEOTYP | ANZ_PERSONEN | ANZ_TITEL | HH_EINKOMMEN_SCORE | W_KEIT_KIND_HH | WOHNDAUER_2008 | ANZ_HAUSHALTE_AKTIV | ANZ_HH_TITEL | GEBAEUDETYP | KONSUMNAEHE | MIN_GEBAEUDEJAHR | OST_WEST_KZ | KBA05_ANTG1 | KBA05_ANTG2 | KBA05_ANTG3 | KBA05_ANTG4 | KBA05_GBZ | BALLRAUM | EWDICHTE | INNENSTADT | GEBAEUDETYP_RASTER | KKK | MOBI_REGIO | ONLINE_AFFINITAET | REGIOTYP | KBA13_ANZAHL_PKW | PLZ8_ANTG1 | PLZ8_ANTG2 | PLZ8_ANTG3 | PLZ8_ANTG4 | PLZ8_HHZ | PLZ8_GBZ | ARBEIT | ORTSGR_KLS9 | RELAT_AB | DECADE | MOVEMENT | WEALTH | LIFE_STAGE_TYPOLOGY | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| count | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 | 7.970770e+05 |
| mean | -2.310333e-16 | -8.441894e-17 | -1.064998e-16 | -1.709506e-16 | -7.889204e-18 | 1.460974e-16 | 7.380195e-17 | -1.123387e-16 | -6.234700e-17 | -3.719960e-17 | -3.146767e-17 | -4.245462e-17 | 3.788601e-17 | -8.584524e-17 | -1.472295e-16 | 1.442610e-16 | -3.652211e-17 | 1.442788e-16 | 3.116459e-17 | -1.132569e-17 | 4.991147e-17 | 4.903564e-17 | 9.685447e-17 | 1.402050e-16 | 1.448516e-16 | 1.194880e-16 | -1.287233e-16 | -3.907162e-17 | -5.409231e-17 | 3.420884e-17 | -1.632218e-16 | 6.329192e-18 | 2.152817e-17 | 7.099392e-17 | -6.692898e-17 | -4.431326e-17 | 4.968861e-17 | -5.509071e-18 | -3.366060e-17 | -2.229480e-16 | 2.092199e-16 | 2.795542e-17 | -5.038393e-17 | -3.904487e-18 | 2.125182e-17 | 5.640112e-17 | 1.836209e-14 | 6.521742e-17 | -5.865646e-17 | -1.212352e-18 | -4.995605e-17 | -5.455585e-18 | 2.264246e-17 | -2.290098e-17 | 8.772617e-17 | -2.068487e-16 | 8.673667e-17 | -9.283409e-17 | -1.400623e-16 | -1.171658e-16 | 1.911238e-17 | -1.205622e-16 | -1.698006e-16 | 2.958362e-16 | -3.354472e-17 | 1.229468e-16 | -1.021050e-16 | -2.972046e-17 | 1.876026e-16 | 1.284113e-16 | -1.087373e-16 | 1.071327e-16 | 8.464626e-17 | -7.484492e-17 | -7.095827e-18 |
| std | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 | 1.000001e+00 |
| min | -1.766173e+00 | -9.581212e-01 | -1.633955e+00 | -1.494463e+00 | -1.155407e+00 | -1.766903e+00 | -1.250019e+00 | -1.184235e+00 | -1.502770e+00 | -1.342762e+00 | -1.814447e+00 | -5.306541e-01 | -1.633876e+00 | -7.175404e-01 | -7.846158e-01 | -1.069778e+00 | -9.602388e-01 | -3.465739e-01 | -1.684892e+00 | -1.618291e+00 | -1.626956e+00 | -1.567172e+00 | -1.508398e+00 | -1.685220e+00 | -1.585320e+00 | -1.982942e+00 | -1.600021e+00 | -1.750430e+00 | -2.016498e+00 | -1.946160e+00 | -1.763585e+00 | -1.716724e+00 | -1.543574e+00 | -1.537143e+00 | -9.211894e-02 | -9.228826e-01 | -1.689869e+00 | -1.495919e+00 | -6.057891e-02 | -2.210438e+00 | -1.854591e+00 | -3.599102e+00 | -4.670539e-01 | -1.251514e-01 | -6.772368e-01 | -1.304315e+00 | -2.515393e+00 | -5.175208e-01 | -1.010076e+00 | -9.668305e-01 | -5.953723e-01 | -4.644322e-01 | -1.658408e+00 | -1.444556e+00 | -1.710675e+00 | -1.749998e+00 | -2.966347e+00 | -1.851980e+00 | -1.281770e+00 | -1.762888e+00 | -1.987075e+00 | -1.800935e+00 | -2.341812e+00 | -3.092277e+00 | -1.647867e+00 | -9.358709e-01 | -2.688305e+00 | -2.159431e+00 | -2.175284e+00 | -1.868305e+00 | -1.526214e+00 | -2.316130e+00 | -1.884467e+00 | -1.551406e+00 | -1.253272e+00 |
| 25% | -7.827197e-01 | -9.581212e-01 | -9.818101e-01 | -7.685472e-01 | -1.155407e+00 | -1.040664e+00 | -1.250019e+00 | -1.184235e+00 | -7.924445e-01 | -8.630920e-01 | -9.766414e-01 | -5.306541e-01 | -3.117925e-01 | -7.175404e-01 | -7.846158e-01 | -1.069778e+00 | -9.602388e-01 | -3.465739e-01 | -9.976668e-01 | -1.103113e+00 | -1.104349e+00 | -5.201781e-01 | -9.858253e-01 | -1.171033e+00 | -1.109821e+00 | -8.880756e-01 | -5.785710e-01 | -5.405937e-01 | -8.799186e-01 | -8.510570e-01 | -6.928596e-01 | -6.384978e-01 | -9.771555e-01 | -5.467051e-01 | -9.211894e-02 | -9.228826e-01 | -2.692462e-01 | -6.311134e-01 | -6.057891e-02 | -9.159636e-01 | -7.296961e-01 | 4.569569e-02 | -4.670539e-01 | -1.251514e-01 | -6.772368e-01 | -6.594552e-01 | -3.831487e-01 | -5.175208e-01 | -1.010076e+00 | -9.668305e-01 | -5.953723e-01 | -4.644322e-01 | -8.873269e-01 | -9.865867e-01 | -1.129027e+00 | -7.639426e-01 | -7.996987e-01 | -7.910934e-01 | -1.281770e+00 | -1.119559e+00 | -8.816575e-01 | -7.000745e-01 | -2.566898e-01 | -8.893524e-01 | -6.223053e-01 | -9.358709e-01 | -6.168824e-01 | -3.374171e-01 | -1.717112e-01 | -5.624970e-01 | -7.892433e-01 | -9.501574e-01 | 5.306541e-01 | -8.695902e-01 | -1.253272e+00 |
| 50% | 2.007334e-01 | -9.581212e-01 | 3.224805e-01 | -4.263094e-02 | 1.913661e-01 | 4.118132e-01 | 1.081913e-01 | -4.694988e-01 | -8.211890e-02 | 9.624855e-02 | 1.404325e-01 | -5.306541e-01 | -3.117925e-01 | -7.175404e-01 | -7.846158e-01 | -2.180454e-01 | -2.986299e-01 | -3.465739e-01 | 3.767838e-01 | -7.275839e-02 | -5.913414e-02 | 3.318662e-03 | 5.931969e-02 | 3.715277e-01 | 3.166748e-01 | -3.406423e-01 | -6.784616e-02 | 6.432461e-02 | 2.566611e-01 | 2.440464e-01 | -1.574969e-01 | -9.938472e-02 | 1.556819e-01 | -5.467051e-01 | -9.211894e-02 | -9.228826e-01 | -2.692462e-01 | -6.311134e-01 | -6.057891e-02 | 3.785112e-01 | 3.951986e-01 | 5.663811e-01 | -2.751370e-01 | -1.251514e-01 | -6.772368e-01 | -1.459572e-02 | -3.831487e-01 | -5.175208e-01 | -2.991561e-01 | -1.633851e-01 | -5.953723e-01 | -4.644322e-01 | -1.162459e-01 | 3.873213e-01 | 3.426866e-02 | 2.221125e-01 | 2.836255e-01 | 2.697932e-01 | 9.133543e-02 | 1.670993e-01 | 2.237603e-01 | -2.209434e-01 | -2.566898e-01 | 2.121098e-01 | 4.032564e-01 | 4.413458e-01 | -6.168824e-01 | -3.374171e-01 | -1.717112e-01 | -1.272278e-01 | -5.227270e-02 | 4.158154e-01 | 5.306541e-01 | 4.940411e-01 | 8.599641e-02 |
| 75% | 1.184186e+00 | 1.043709e+00 | 9.746258e-01 | 6.832853e-01 | 8.647525e-01 | 1.138052e+00 | 7.872963e-01 | 9.599741e-01 | 6.282067e-01 | 1.055589e+00 | 9.782380e-01 | -5.306541e-01 | 1.010291e+00 | 1.077262e+00 | 9.933998e-01 | 1.201510e+00 | 1.024588e+00 | -3.465739e-01 | 1.064009e+00 | 9.575964e-01 | 9.860802e-01 | 5.268155e-01 | 5.818922e-01 | 8.857147e-01 | 7.921735e-01 | 7.542242e-01 | 9.536035e-01 | 6.692430e-01 | 8.249510e-01 | 7.915981e-01 | 9.132284e-01 | 9.788414e-01 | 7.221006e-01 | 4.437331e-01 | -9.211894e-02 | 1.083561e+00 | 4.410653e-01 | 2.336919e-01 | -6.057891e-02 | 1.025749e+00 | 9.576459e-01 | 5.663811e-01 | 4.472452e-02 | -1.251514e-01 | 7.532385e-02 | 6.302637e-01 | -7.854226e-02 | -5.175208e-01 | 1.122684e+00 | 6.400604e-01 | 4.072339e-01 | -4.644322e-01 | 6.548351e-01 | 8.452907e-01 | 1.197564e+00 | 7.151400e-01 | 2.836255e-01 | 2.697932e-01 | 7.778883e-01 | 8.104284e-01 | 7.764692e-01 | 4.606776e-01 | 7.858712e-01 | 2.121098e-01 | 4.032564e-01 | 4.413458e-01 | 4.188286e-01 | 5.735896e-01 | 8.300750e-01 | 7.433107e-01 | 6.846979e-01 | 1.098802e+00 | 5.306541e-01 | 1.175857e+00 | 7.556306e-01 |
| max | 1.184186e+00 | 1.043709e+00 | 1.626771e+00 | 1.409202e+00 | 1.538139e+00 | 1.138052e+00 | 1.466401e+00 | 1.674710e+00 | 1.338532e+00 | 1.055589e+00 | 1.257506e+00 | 1.884467e+00 | 1.010291e+00 | 1.846462e+00 | 1.586072e+00 | 1.485421e+00 | 1.686197e+00 | 3.946528e+00 | 1.064009e+00 | 1.472774e+00 | 1.508687e+00 | 1.573809e+00 | 1.627037e+00 | 1.399902e+00 | 1.267672e+00 | 1.301657e+00 | 1.464328e+00 | 1.879080e+00 | 1.393241e+00 | 1.339150e+00 | 1.448591e+00 | 1.517954e+00 | 1.854938e+00 | 1.434171e+00 | 1.085553e+01 | 1.083561e+00 | 1.861688e+00 | 3.742032e+01 | 5.830879e+01 | 1.025749e+00 | 9.576459e-01 | 5.663811e-01 | 3.753249e+01 | 7.099171e+01 | 1.956725e+00 | 2.564842e+00 | 6.927405e+00 | 1.932289e+00 | 1.833604e+00 | 2.246951e+00 | 2.412446e+00 | 2.728773e+00 | 1.425916e+00 | 1.303260e+00 | 1.197564e+00 | 1.701195e+00 | 1.366950e+00 | 1.330680e+00 | 2.150994e+00 | 1.453757e+00 | 1.329178e+00 | 4.758598e+00 | 1.828432e+00 | 1.313572e+00 | 1.428818e+00 | 1.818562e+00 | 1.454540e+00 | 1.484596e+00 | 1.831861e+00 | 1.613849e+00 | 1.421668e+00 | 1.098802e+00 | 5.306541e-01 | 1.175857e+00 | 1.425265e+00 |
Discussion 2.1: Apply Feature Scaling¶
used a StandardScaler instance, scaling each feature to mean 0 and standard 1
df_sc.shape
(797077, 75)
Step 2.2: Perform Dimensionality Reduction¶
On your scaled data, you are now ready to apply dimensionality reduction techniques.
- Use sklearn's PCA class to apply principal component analysis on the data, thus finding the vectors of maximal variance in the data. To start, you should not set any parameters (so all components are computed) or set a number of components that is at least half the number of features (so there's enough features to see the general trend in variability).
- Check out the ratio of variance explained by each principal component as well as the cumulative variance explained. Try plotting the cumulative or sequential values using matplotlib's
plot()function. Based on what you find, select a value for the number of transformed features you'll retain for the clustering part of the project. - Once you've made a choice for the number of components to keep, make sure you re-fit a PCA instance to perform the decided-on transformation.
# Apply PCA to the data.
pca= PCA()
pca = pca.fit(df_sc)
def scree_plot(pca):
'''
Creates a scree plot associated with the principal components
INPUT: pca - the result of instantian of PCA in scikit learn
OUTPUT:
None
'''
num_components=len(pca.explained_variance_ratio_)
ind = np.arange(num_components)
vals = pca.explained_variance_ratio_
plt.figure(figsize=(25, 10))
ax = plt.subplot(111)
cumvals = np.cumsum(vals)
ax.bar(ind, vals)
ax.plot(ind, cumvals)
for i in range(num_components):
ax.annotate(r"%s%%" % ((str(vals[i]*100)[:4])), (ind[i]+0.2, vals[i]), va="bottom", ha="center", fontsize=8)
ax.xaxis.set_tick_params(width=0)
ax.yaxis.set_tick_params(width=2, length=12)
ax.set_xlabel("Principal Component")
ax.set_ylabel("Variance-Explained Ratio")
plt.title('Explained Variance Per Principal Component')
# Investigate the variance accounted for by each principal component.
scree_plot(pca)
# Re-apply PCA to the data while selecting for number of components to retain.
pca_35 = PCA(n_components=35)
features_pca = pca_35.fit_transform(df_sc)
Discussion 2.2: Perform Dimensionality Reduction¶
Used only 35 Components and this retains more than 80% of data variability
Step 2.3: Interpret Principal Components¶
Now that we have our transformed principal components, it's a nice idea to check out the weight of each variable on the first few components to see if they can be interpreted in some fashion.
As a reminder, each principal component is a unit vector that points in the direction of highest variance (after accounting for the variance captured by earlier principal components). The further a weight is from zero, the more the principal component is in the direction of the corresponding feature. If two features have large weights of the same sign (both positive or both negative), then increases in one tend expect to be associated with increases in the other. To contrast, features with different signs can be expected to show a negative correlation: increases in one variable should result in a decrease in the other.
- To investigate the features, you should map each weight to their corresponding feature name, then sort the features according to weight. The most interesting features for each principal component, then, will be those at the beginning and end of the sorted list. Use the data dictionary document to help you understand these most prominent features, their relationships, and what a positive or negative value on the principal component might indicate.
- You should investigate and interpret feature associations from the first three principal components in this substep. To help facilitate this, you should write a function that you can call at any time to print the sorted list of feature weights, for the i-th principal component. This might come in handy in the next step of the project, when you interpret the tendencies of the discovered clusters.
d = pd.DataFrame(pca.components_, columns=pca.feature_names_in_)
# Map weights for the first principal component to corresponding feature names
# and then print the linked values, sorted by weight.
# HINT: Try defining a function here or in a new cell that you can reuse in the
# other cells.
def get_features_weights(pca, principal_component):
weights = pd.DataFrame(pca.components_, columns=pca.feature_names_in_).iloc[principal_component]
weights.sort_values(ascending=False, inplace=True)
return weights
get_features_weights(pca_35, 0)
PLZ8_ANTG3 0.202463 HH_EINKOMMEN_SCORE 0.200525 PLZ8_ANTG4 0.194955 WEALTH 0.190943 ORTSGR_KLS9 0.173692 EWDICHTE 0.172053 FINANZ_HAUSBAUER 0.162667 KBA05_ANTG4 0.142546 ZABEOTYP 0.138781 PLZ8_ANTG2 0.138755 FINANZ_SPARER 0.136884 KBA05_ANTG3 0.131142 ANZ_HAUSHALTE_AKTIV 0.127069 ARBEIT 0.126217 RELAT_AB 0.119590 MOVEMENT 0.116436 SEMIO_PFLICHT 0.104376 SEMIO_REL 0.098978 DECADE 0.096911 SEMIO_RAT 0.088039 SEMIO_TRADV 0.078800 GEBAEUDETYP 0.071183 FINANZ_UNAUFFAELLIGER 0.069220 FINANZ_ANLEGER 0.068919 SEMIO_MAT 0.068511 GFK_URLAUBERTYP 0.065041 SEMIO_FAM 0.063852 SEMIO_KULT 0.056960 REGIOTYP 0.056560 NATIONALITAET_KZ 0.055843 OST_WEST_KZ 0.047274 W_KEIT_KIND_HH 0.046416 CJT_GESAMTTYP 0.045479 SEMIO_KAEM 0.043571 PLZ8_HHZ 0.039261 KKK 0.039079 HEALTH_TYP 0.037430 SEMIO_SOZ 0.030222 ANZ_HH_TITEL 0.030154 SEMIO_DOM 0.027872 KBA05_ANTG2 0.017750 SEMIO_KRIT 0.015031 SOHO_KZ -0.002251 ANZ_TITEL -0.005473 SHOPPER_TYP -0.009552 RETOURTYP_BK_S -0.016409 ANREDE_KZ -0.016742 VERS_TYP -0.031564 MIN_GEBAEUDEJAHR -0.044985 SEMIO_VERT -0.047283 ONLINE_AFFINITAET -0.051242 WOHNDAUER_2008 -0.059100 SEMIO_ERL -0.061847 KBA13_ANZAHL_PKW -0.064754 FINANZTYP -0.065201 SEMIO_LUST -0.065287 LP_FAMILIE_FEIN -0.085653 LP_FAMILIE_GROB -0.087429 ANZ_PERSONEN -0.088204 FINANZ_VORSORGER -0.103306 GEBAEUDETYP_RASTER -0.106071 ALTERSKATEGORIE_GROB -0.107049 BALLRAUM -0.110738 LIFE_STAGE_TYPOLOGY -0.114973 GREEN_AVANTGARDE -0.116436 INNENSTADT -0.144319 PLZ8_GBZ -0.148180 KONSUMNAEHE -0.150795 KBA05_ANTG1 -0.201369 KBA05_GBZ -0.202074 PLZ8_ANTG1 -0.202915 MOBI_REGIO -0.209710 FINANZ_MINIMALIST -0.216868 LP_STATUS_GROB -0.227282 LP_STATUS_FEIN -0.227380 Name: 0, dtype: float64
# Map weights for the second principal component to corresponding feature names
# and then print the linked values, sorted by weight.
get_features_weights(pca_35, 1)
ALTERSKATEGORIE_GROB 0.255069 FINANZ_VORSORGER 0.228016 SEMIO_ERL 0.222073 SEMIO_LUST 0.177622 RETOURTYP_BK_S 0.160787 W_KEIT_KIND_HH 0.121259 FINANZ_HAUSBAUER 0.110319 SEMIO_KRIT 0.109867 FINANZTYP 0.109242 SHOPPER_TYP 0.104887 SEMIO_KAEM 0.102485 PLZ8_ANTG3 0.083165 EWDICHTE 0.081655 PLZ8_ANTG4 0.081468 ORTSGR_KLS9 0.080511 FINANZ_MINIMALIST 0.070026 WEALTH 0.068366 KBA05_ANTG4 0.065781 SEMIO_DOM 0.064192 ARBEIT 0.062283 RELAT_AB 0.058467 ANZ_HAUSHALTE_AKTIV 0.058441 PLZ8_ANTG2 0.057089 WOHNDAUER_2008 0.056117 HH_EINKOMMEN_SCORE 0.054124 KBA05_ANTG3 0.043437 ANZ_HH_TITEL 0.029295 OST_WEST_KZ 0.028101 MOVEMENT 0.017583 GEBAEUDETYP 0.016704 PLZ8_HHZ 0.012453 REGIOTYP 0.008201 ANZ_TITEL 0.006120 LIFE_STAGE_TYPOLOGY 0.003883 KBA05_ANTG2 -0.000909 KKK -0.001647 SOHO_KZ -0.002035 ZABEOTYP -0.012006 GREEN_AVANTGARDE -0.017583 VERS_TYP -0.026473 LP_STATUS_GROB -0.027215 KBA13_ANZAHL_PKW -0.033046 GFK_URLAUBERTYP -0.033734 GEBAEUDETYP_RASTER -0.040319 MIN_GEBAEUDEJAHR -0.045702 LP_STATUS_FEIN -0.046233 BALLRAUM -0.053428 HEALTH_TYP -0.057050 SEMIO_VERT -0.061026 KONSUMNAEHE -0.063158 KBA05_ANTG1 -0.063605 PLZ8_GBZ -0.064474 INNENSTADT -0.066484 NATIONALITAET_KZ -0.067412 MOBI_REGIO -0.068692 ANZ_PERSONEN -0.074214 KBA05_GBZ -0.079498 ANREDE_KZ -0.081394 LP_FAMILIE_FEIN -0.081708 PLZ8_ANTG1 -0.082137 LP_FAMILIE_GROB -0.083130 SEMIO_SOZ -0.095915 CJT_GESAMTTYP -0.124383 SEMIO_MAT -0.156507 ONLINE_AFFINITAET -0.168867 SEMIO_RAT -0.170135 SEMIO_FAM -0.176008 FINANZ_ANLEGER -0.202591 SEMIO_KULT -0.210367 SEMIO_PFLICHT -0.225214 SEMIO_TRADV -0.225275 FINANZ_UNAUFFAELLIGER -0.226493 FINANZ_SPARER -0.234015 DECADE -0.239448 SEMIO_REL -0.249845 Name: 1, dtype: float64
# Map weights for the third principal component to corresponding feature names
# and then print the linked values, sorted by weight.
get_features_weights(pca_35, 2)
ANREDE_KZ 0.362304 SEMIO_VERT 0.335494 SEMIO_SOZ 0.260058 SEMIO_FAM 0.254202 SEMIO_KULT 0.240607 FINANZ_MINIMALIST 0.142352 FINANZTYP 0.114726 RETOURTYP_BK_S 0.102427 W_KEIT_KIND_HH 0.087106 FINANZ_VORSORGER 0.086220 SEMIO_REL 0.082626 SEMIO_MAT 0.064778 ALTERSKATEGORIE_GROB 0.062423 PLZ8_ANTG4 0.055788 ORTSGR_KLS9 0.055372 PLZ8_ANTG3 0.055002 EWDICHTE 0.053721 SEMIO_LUST 0.051449 ARBEIT 0.043548 GREEN_AVANTGARDE 0.039979 RELAT_AB 0.037806 WEALTH 0.037426 PLZ8_ANTG2 0.036156 KBA05_ANTG4 0.033985 ANZ_HAUSHALTE_AKTIV 0.030513 LP_STATUS_GROB 0.026583 WOHNDAUER_2008 0.026022 OST_WEST_KZ 0.023063 KBA05_ANTG3 0.015635 ANZ_HH_TITEL 0.014149 GEBAEUDETYP 0.013053 LP_STATUS_FEIN 0.011432 ANZ_TITEL 0.008851 PLZ8_HHZ 0.005494 SOHO_KZ 0.000119 REGIOTYP -0.005266 KBA05_ANTG2 -0.005640 VERS_TYP -0.005703 HH_EINKOMMEN_SCORE -0.008499 KKK -0.013235 LIFE_STAGE_TYPOLOGY -0.016807 GFK_URLAUBERTYP -0.017855 NATIONALITAET_KZ -0.019092 MIN_GEBAEUDEJAHR -0.019238 ANZ_PERSONEN -0.020298 HEALTH_TYP -0.026378 KBA13_ANZAHL_PKW -0.027700 KBA05_ANTG1 -0.028520 LP_FAMILIE_FEIN -0.031130 MOBI_REGIO -0.032856 LP_FAMILIE_GROB -0.033014 KBA05_GBZ -0.034014 CJT_GESAMTTYP -0.036091 GEBAEUDETYP_RASTER -0.036175 FINANZ_HAUSBAUER -0.037454 MOVEMENT -0.039979 BALLRAUM -0.040034 KONSUMNAEHE -0.044949 PLZ8_GBZ -0.046925 INNENSTADT -0.050255 ZABEOTYP -0.054568 ONLINE_AFFINITAET -0.056298 PLZ8_ANTG1 -0.057025 SEMIO_PFLICHT -0.063915 SEMIO_TRADV -0.064992 FINANZ_SPARER -0.092006 FINANZ_UNAUFFAELLIGER -0.093732 DECADE -0.097740 SHOPPER_TYP -0.123121 FINANZ_ANLEGER -0.176582 SEMIO_ERL -0.184397 SEMIO_RAT -0.201295 SEMIO_KRIT -0.271099 SEMIO_DOM -0.307329 SEMIO_KAEM -0.330173 Name: 2, dtype: float64
Discussion 2.3: Interpret Principal Components¶
First principal component
positive values:
- PLZ8_ANTG3(Number of 6-10 family houses in the PLZ8 region)
- HH_EINKOMMEN_SCORE (Estimated household net income)
- WEALTH(Households wealth)
- PLZ8_ANTG4 ( Number of 10+ family houses in the PLZ8 region)
negative values:
- FINANZ_MINIMALIST(Financial typology:low financial interest)
- MOBI_REGIO(Movement patterns)
- PLZ8_ANTG1(Number of 1-2 family houses in the PLZ8 region)
- KBA05_GBZ(Number of buildings in the microcell)
- LP_STATUS_GROB(Social status, rough scale)
- LP_STATUS_FEIN(Social status, fine scale)
based on the observations, the first component has a correlation with a number of buildings, family (family members), social status, household net income, and wealth.
Second principal component
positive values:
- ALTERSKATEGORIE_GROB(Estimated age based on given name analysis)
- FINANZ_VORSORGER(Financial typology:be prepared)
- SEMIO_LUST(Personality typology)
- SEMIO_ERL(Personality typology:event-oriented)
negative values:
- DECADE(generation)
- FINANZ_SPARER(Financial typology:money-saver)
- FINANZ_UNAUFFAELLIGER(Financial typology:inconspicuous)
- SEMIO_REL(Personality typology:religious)
- SEMIO_TRADV(Personality typology)
based on the observations, second component is related to Financial typology, Personality typology, age, energy consumption, and decade.
Third principal component
positive values:
- ANREDE_KZ(Gender:femal,male)
- SEMIO_VERT(Personality typology:dreamful)
- SEMIO_FAM (Personality typology:family-minded)
- SEMIO_SOZ (Personality typology:socially-minded)
negative values:
- SEMIO_KAEM (Personality typology:combative attitude)
- SEMIO_DOM (Personality typology:dominant-minded)
- SEMIO_KRIT (Personality typology:critical-minded)
- SEMIO_ERL (Personality typology:event-oriented)
based on the observations, third component has a strong relationship with Personality typology and gender.
Step 3: Clustering¶
Step 3.1: Apply Clustering to General Population¶
You've assessed and cleaned the demographics data, then scaled and transformed them. Now, it's time to see how the data clusters in the principal components space. In this substep, you will apply k-means clustering to the dataset and use the average within-cluster distances from each point to their assigned cluster's centroid to decide on a number of clusters to keep.
- Use sklearn's KMeans class to perform k-means clustering on the PCA-transformed data.
- Then, compute the average difference from each point to its assigned cluster's center. Hint: The KMeans object's
.score()method might be useful here, but note that in sklearn, scores tend to be defined so that larger is better. Try applying it to a small, toy dataset, or use an internet search to help your understanding. - Perform the above two steps for a number of different cluster counts. You can then see how the average distance decreases with an increasing number of clusters. However, each additional cluster provides a smaller net benefit. Use this fact to select a final number of clusters in which to group the data. Warning: because of the large size of the dataset, it can take a long time for the algorithm to resolve. The more clusters to fit, the longer the algorithm will take. You should test for cluster counts through at least 10 clusters to get the full picture, but you shouldn't need to test for a number of clusters above about 30.
- Once you've selected a final number of clusters to use, re-fit a KMeans instance to perform the clustering operation. Make sure that you also obtain the cluster assignments for the general demographics data, since you'll be using them in the final Step 3.3.
def kmenas_run(df, n_clusters):
kmeans = KMeans(n_clusters=n_clusters)
kmean_model = kmeans.fit(df)
score = np.abs(kmean_model.score(df))
return score
# Over a number of different cluster counts...
centroides = list(range(10, 25))
result_scores = []
for centroide in centroides:
# run k-means clustering on the data and...
# compute the average within-cluster distances.
result_scores.append(kmenas_run(features_pca,centroide))
# Investigate the change in within-cluster distance across number of clusters.
# HINT: Use matplotlib's plot function to visualize this relationship.
def plot_Kmeans_score(scores, centroides):
plt.figure(figsize=(10, 6))
plt.plot(centroides,scores, marker='d',linestyle='--', color='g')
plt.xlabel('number of clusters (K)')
plt.ylabel('Average distance form centroide')
plt.title("Relationship between K and Avg distance from centroide")
plt.show()
plot_Kmeans_score(result_scores, centroides)
# Re-fit the k-means model with the selected number of clusters and obtain
# cluster predictions for the general population demographics data.
kmeans = KMeans(n_clusters=22)
kmean_model = kmeans.fit(features_pca)
preds=kmean_model.predict(features_pca)
Discussion 3.1: Apply Clustering to General Population¶
Based on the observation we saw in the plot we can't see the elbow but we can see the score decreases with the number of clusters increase between 18-22 clusters so it seems that 22 is a good number of clusters to proceed with .
Step 3.2: Apply All Steps to the Customer Data¶
Now that you have clusters and cluster centers for the general population, it's time to see how the customer data maps on to those clusters. Take care to not confuse this for re-fitting all of the models to the customer data. Instead, you're going to use the fits from the general population to clean, transform, and cluster the customer data. In the last step of the project, you will interpret how the general population fits apply to the customer data.
- Don't forget when loading in the customers data, that it is semicolon (
;) delimited. - Apply the same feature wrangling, selection, and engineering steps to the customer demographics using the
clean_data()function you created earlier. (You can assume that the customer demographics data has similar meaning behind missing data patterns as the general demographics data.) - Use the sklearn objects from the general demographics data, and apply their transformations to the customers data. That is, you should not be using a
.fit()or.fit_transform()method to re-fit the old objects, nor should you be creating new sklearn objects! Carry the data through the feature scaling, PCA, and clustering steps, obtaining cluster assignments for all of the data in the customer demographics data.
# Load in the customer demographics data.
customers = pd.read_csv('Udacity_CUSTOMERS_Subset.csv',sep=';')
feat_info = pd.read_csv("AZDIAS_Feature_Summary.csv", sep=';')
customers.head()
| AGER_TYP | ALTERSKATEGORIE_GROB | ANREDE_KZ | CJT_GESAMTTYP | FINANZ_MINIMALIST | FINANZ_SPARER | FINANZ_VORSORGER | FINANZ_ANLEGER | FINANZ_UNAUFFAELLIGER | FINANZ_HAUSBAUER | FINANZTYP | GEBURTSJAHR | GFK_URLAUBERTYP | GREEN_AVANTGARDE | HEALTH_TYP | LP_LEBENSPHASE_FEIN | LP_LEBENSPHASE_GROB | LP_FAMILIE_FEIN | LP_FAMILIE_GROB | LP_STATUS_FEIN | LP_STATUS_GROB | NATIONALITAET_KZ | PRAEGENDE_JUGENDJAHRE | RETOURTYP_BK_S | SEMIO_SOZ | SEMIO_FAM | SEMIO_REL | SEMIO_MAT | SEMIO_VERT | SEMIO_LUST | SEMIO_ERL | SEMIO_KULT | SEMIO_RAT | SEMIO_KRIT | SEMIO_DOM | SEMIO_KAEM | SEMIO_PFLICHT | SEMIO_TRADV | SHOPPER_TYP | SOHO_KZ | TITEL_KZ | VERS_TYP | ZABEOTYP | ALTER_HH | ANZ_PERSONEN | ANZ_TITEL | HH_EINKOMMEN_SCORE | KK_KUNDENTYP | W_KEIT_KIND_HH | WOHNDAUER_2008 | ANZ_HAUSHALTE_AKTIV | ANZ_HH_TITEL | GEBAEUDETYP | KONSUMNAEHE | MIN_GEBAEUDEJAHR | OST_WEST_KZ | WOHNLAGE | CAMEO_DEUG_2015 | CAMEO_DEU_2015 | CAMEO_INTL_2015 | KBA05_ANTG1 | KBA05_ANTG2 | KBA05_ANTG3 | KBA05_ANTG4 | KBA05_BAUMAX | KBA05_GBZ | BALLRAUM | EWDICHTE | INNENSTADT | GEBAEUDETYP_RASTER | KKK | MOBI_REGIO | ONLINE_AFFINITAET | REGIOTYP | KBA13_ANZAHL_PKW | PLZ8_ANTG1 | PLZ8_ANTG2 | PLZ8_ANTG3 | PLZ8_ANTG4 | PLZ8_BAUMAX | PLZ8_HHZ | PLZ8_GBZ | ARBEIT | ORTSGR_KLS9 | RELAT_AB | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 2 | 4 | 1 | 5.0 | 5 | 1 | 5 | 1 | 2 | 2 | 2 | 0 | 4.0 | 1 | 1 | 20.0 | 5.0 | 2.0 | 2.0 | 10.0 | 5.0 | 1 | 4 | 5.0 | 6 | 5 | 2 | 6 | 6 | 7 | 3 | 4 | 1 | 3 | 1 | 1 | 2 | 1 | 3 | 0.0 | 0.0 | 1 | 3 | 10.0 | 2.0 | 0.0 | 1.0 | NaN | 6.0 | 9.0 | 1.0 | 0.0 | 1.0 | 5.0 | 1992.0 | W | 7.0 | 1 | 1A | 13 | 2.0 | 2.0 | 0.0 | 0.0 | 0.0 | 4.0 | 3.0 | 2.0 | 4.0 | 4.0 | 1.0 | 4.0 | 3.0 | 1.0 | 1201.0 | 3.0 | 3.0 | 1.0 | 0.0 | 1.0 | 5.0 | 5.0 | 1.0 | 2.0 | 1.0 |
| 1 | -1 | 4 | 1 | NaN | 5 | 1 | 5 | 1 | 3 | 2 | 2 | 0 | NaN | 0 | 1 | NaN | NaN | NaN | NaN | NaN | NaN | 1 | 0 | NaN | 3 | 6 | 2 | 6 | 7 | 5 | 3 | 4 | 1 | 3 | 3 | 2 | 4 | 1 | 3 | 0.0 | 0.0 | 1 | 3 | 11.0 | 3.0 | 0.0 | NaN | NaN | 0.0 | 9.0 | NaN | NaN | NaN | 5.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN | NaN |
| 2 | -1 | 4 | 2 | 2.0 | 5 | 1 | 5 | 1 | 4 | 4 | 2 | 0 | 3.0 | 1 | 2 | 13.0 | 3.0 | 1.0 | 1.0 | 10.0 | 5.0 | 1 | 4 | 5.0 | 2 | 2 | 1 | 3 | 3 | 7 | 7 | 1 | 2 | 7 | 5 | 6 | 4 | 1 | 1 | 0.0 | 0.0 | 2 | 3 | 6.0 | 1.0 | 0.0 | 1.0 | NaN | 6.0 | 9.0 | 1.0 | 0.0 | 8.0 | 1.0 | 1992.0 | W | 2.0 | 5 | 5D | 34 | 2.0 | 2.0 | 0.0 | 0.0 | 0.0 | 3.0 | 7.0 | 4.0 | 1.0 | 3.0 | 3.0 | 3.0 | 1.0 | 7.0 | 433.0 | 2.0 | 3.0 | 3.0 | 1.0 | 3.0 | 3.0 | 2.0 | 3.0 | 5.0 | 3.0 |
| 3 | 1 | 4 | 1 | 2.0 | 5 | 1 | 5 | 2 | 1 | 2 | 6 | 0 | 10.0 | 0 | 2 | 0.0 | 0.0 | 0.0 | 0.0 | 9.0 | 4.0 | 1 | 1 | 3.0 | 6 | 5 | 3 | 4 | 7 | 5 | 3 | 4 | 3 | 3 | 3 | 3 | 3 | 4 | 0 | 0.0 | 0.0 | 1 | 1 | 8.0 | 0.0 | 0.0 | 4.0 | NaN | NaN | 9.0 | 0.0 | NaN | 2.0 | 2.0 | 1992.0 | W | 7.0 | 4 | 4C | 24 | 3.0 | 0.0 | 0.0 | 0.0 | 1.0 | 4.0 | 7.0 | 1.0 | 7.0 | 4.0 | 3.0 | 4.0 | 2.0 | 6.0 | 755.0 | 3.0 | 2.0 | 1.0 | 0.0 | 1.0 | 3.0 | 4.0 | 1.0 | 3.0 | 1.0 |
| 4 | -1 | 3 | 1 | 6.0 | 3 | 1 | 4 | 4 | 5 | 2 | 2 | 1960 | 2.0 | 0 | 3 | 31.0 | 10.0 | 10.0 | 5.0 | 1.0 | 1.0 | 1 | 8 | 5.0 | 4 | 5 | 4 | 6 | 5 | 6 | 4 | 5 | 5 | 3 | 5 | 2 | 5 | 4 | 1 | 0.0 | 0.0 | 2 | 1 | 20.0 | 4.0 | 0.0 | 6.0 | 2.0 | 2.0 | 9.0 | 7.0 | 0.0 | 3.0 | 1.0 | 1992.0 | W | 3.0 | 7 | 7B | 41 | 0.0 | 3.0 | 2.0 | 0.0 | 0.0 | 3.0 | 3.0 | 4.0 | 4.0 | 3.0 | 4.0 | 3.0 | 5.0 | 7.0 | 513.0 | 2.0 | 4.0 | 2.0 | 1.0 | 2.0 | 3.0 | 3.0 | 3.0 | 5.0 | 1.0 |
# Apply preprocessing, feature transformation, and clustering from the general
# demographics onto the customer data, obtaining cluster predictions for the
# customer demographics data.
clean_customers = clean_data(customers, feat_info)
def add_missing_dummy_columns(df, columns):
missing_cols = set(columns) - set(df.columns)
print(missing_cols)
for c in missing_cols:
df[c] = 0
def fix_columns(d, columns):
add_missing_dummy_columns(df, columns)
# make sure we have all the columns we need
assert(set(columns) - set(df.columns) == set())
extra_cols = set(df.columns) - set(columns)
if extra_cols:
print("extra columns:", extra_cols)
df = df[columns]
return df
clean_customers = clean_customers.select_dtypes(['float', 'int'])
clean_customers.isnull().sum().sum()
0
customer_sc = sc.transform(clean_customers)
customer_sc = pd.DataFrame(data=customer_sc, columns=clean_customers.columns.tolist())
customer_sc.shape
(141701, 75)
customers_pca=pca_35.transform(customer_sc)
pred_customers= kmean_model.predict(customers_pca)
Step 3.3: Compare Customer Data to Demographics Data¶
At this point, you have clustered data based on demographics of the general population of Germany, and seen how the customer data for a mail-order sales company maps onto those demographic clusters. In this final substep, you will compare the two cluster distributions to see where the strongest customer base for the company is.
Consider the proportion of persons in each cluster for the general population, and the proportions for the customers. If we think the company's customer base to be universal, then the cluster assignment proportions should be fairly similar between the two. If there are only particular segments of the population that are interested in the company's products, then we should see a mismatch from one to the other. If there is a higher proportion of persons in a cluster for the customer data compared to the general population (e.g. 5% of persons are assigned to a cluster for the general population, but 15% of the customer data is closest to that cluster's centroid) then that suggests the people in that cluster to be a target audience for the company. On the other hand, the proportion of the data in a cluster being larger in the general population than the customer data (e.g. only 2% of customers closest to a population centroid that captures 6% of the data) suggests that group of persons to be outside of the target demographics.
Take a look at the following points in this step:
- Compute the proportion of data points in each cluster for the general population and the customer data. Visualizations will be useful here: both for the individual dataset proportions, but also to visualize the ratios in cluster representation between groups. Seaborn's
countplot()orbarplot()function could be handy.- Recall the analysis you performed in step 1.1.3 of the project, where you separated out certain data points from the dataset if they had more than a specified threshold of missing values. If you found that this group was qualitatively different from the main bulk of the data, you should treat this as an additional data cluster in this analysis. Make sure that you account for the number of data points in this subset, for both the general population and customer datasets, when making your computations!
- Which cluster or clusters are overrepresented in the customer dataset compared to the general population? Select at least one such cluster and infer what kind of people might be represented by that cluster. Use the principal component interpretations from step 2.3 or look at additional components to help you make this inference. Alternatively, you can use the
.inverse_transform()method of the PCA and StandardScaler objects to transform centroids back to the original data space and interpret the retrieved values directly. - Perform a similar investigation for the underrepresented clusters. Which cluster or clusters are underrepresented in the customer dataset compared to the general population, and what kinds of people are typified by these clusters?
# Compare the proportion of data in each cluster for the customer data to the
# proportion of data in each cluster for the general population.
def difference_plot(general, customers, pca_customers, feature_names, pca, scaler, centroids):
"""
Parameters:
- general: Array of cluster labels for the general population.
- customers: Array of cluster labels for the customer dataset.
- pca_customers: PCA-transformed customer data.
- preds_customers: Predicted cluster labels for customers.
- feature_names: List of original feature names (for interpretation).
- pca: Fitted PCA object.
- scaler: Fitted StandardScaler object.
- centroids: Cluster centroids in PCA space.
- azdias_columns: Column names of the original dataset (e.g., azdias_below20.columns).
Returns:
- overrepresented_clusters: List of clusters where customer proportion > general proportion.
- underrepresented_clusters: List of clusters where customer proportion < general proportion.
- cluster_data: Dictionary of DataFrames with original data per cluster.
- centroid_df: DataFrame of centroid values in original space.
"""
# Input validation
if len(general) == 0 or len(customers) == 0:
raise ValueError("Input arrays cannot be empty.")
# Get unique clusters and counts
all_clusters = np.unique(np.concatenate([general, customers]))
general_counts = pd.Series(general).value_counts().reindex(all_clusters, fill_value=0)
customers_counts = pd.Series(customers).value_counts().reindex(all_clusters, fill_value=0)
# Calculate proportions
general_proportions = general_counts / len(general) * 100
customers_proportions = customers_counts / len(customers) * 100
# Identify overrepresented clusters
overrepresentation_ratio = customers_proportions / general_proportions
overrepresented_clusters = overrepresentation_ratio[overrepresentation_ratio > 1].index.tolist()
underrepresented_clusters = overrepresentation_ratio[overrepresentation_ratio < 1].index.tolist()
# Create DataFrame for plotting
plot_data = pd.DataFrame({
'Cluster': all_clusters,
'General': general_proportions,
'Customers': customers_proportions
}).melt(id_vars='Cluster', value_vars=['General', 'Customers'], var_name='Dataset', value_name='Percentage')
# Plot grouped bar chart
plt.figure(figsize=(10, 6))
sns.barplot(x='Cluster', y='Percentage', hue='Dataset', data=plot_data, palette=['skyblue', 'salmon'])
plt.title('Cluster Proportions: General Population vs. Customers', fontsize=12)
plt.xlabel('Cluster', fontsize=10)
plt.ylabel('Percentage (%)', fontsize=10)
plt.xticks(rotation=45, ha='right')
plt.grid(True, axis='y', linestyle='--', alpha=0.7)
plt.legend(title='Dataset')
plt.tight_layout()
plt.show()
# Print overrepresentation
print("\nOverrepresented clusters (ratio > 1):")
if overrepresented_clusters:
for cluster in overrepresented_clusters:
ratio = overrepresentation_ratio[cluster]
print(f"Cluster {cluster}: Customer Proportion = {customers_proportions[cluster]:.2f}%, "
f"General Proportion = {general_proportions[cluster]:.2f}%, Ratio = {ratio:.2f}")
else:
print("None")
# Print underrepresented clusters
print("\nUnderrepresented clusters (ratio < 1):")
if underrepresented_clusters:
for cluster in underrepresented_clusters:
ratio = overrepresentation_ratio[cluster]
print(f"Cluster {cluster}: Customer Proportion = {customers_proportions[cluster]:.2f}%, "
f"General Proportion = {general_proportions[cluster]:.2f}%, Ratio = {ratio:.2f}")
else:
print("None")
########################################################################################
# Extracting the Clusters Data
cluster_data = {}
# Interpret overrepresented clusters (if PCA and scaler are provided)
if (overrepresented_clusters or underrepresented_clusters) and feature_names is not None and pca is not None and scaler is not None and centroids is not None:
for cluster in all_clusters:
mask = (cluster == customers)
extracted_data = scaler.inverse_transform(np.dot(pca_customers[mask], pca.components_))
cluster_data[cluster] = pd.DataFrame(extracted_data, columns=feature_names)
centroid_std = pca.inverse_transform(centroids)
centroid_orig = scaler.inverse_transform(centroid_std)
centroid_df = pd.DataFrame(centroid_orig, columns=feature_names)
loadings = pd.DataFrame(pca.components_.T, columns=[f'PC{i+1}' for i in range(pca.n_components_)],
index=feature_names)
# Prominent features from PCA loadings (e.g., top 3 from Component 1)
loadings = pd.DataFrame(pca.components_.T, columns=[f'PC{i+1}' for i in range(pca.n_components_)], index=feature_names)
prominent_features = loadings.abs().nlargest(4, columns=['PC1']).index.tolist() # Adjust based on your plot
# Create distribution plots for prominent features in overrepresented clusters
if overrepresented_clusters:
print("\nDistribution Plots for Prominent Features in Overrepresented Clusters:")
print(prominent_features, "\n")
for cluster in overrepresented_clusters:
# Create figure with subplots (number of subplots matches the number of prominent features)
num_features = len(prominent_features)
fig, ax = plt.subplots(1, num_features, figsize=(5 * num_features, 5)) # Adjust figsize based on number of features
# Ensure ax is iterable even if num_features is 1
ax = ax.flatten() if num_features > 1 else [ax]
# Plot distribution for each prominent feature
for i, feature in enumerate(prominent_features):
sns.histplot(data=cluster_data[cluster], x=feature, bins=20, kde=True, ax=ax[i])
ax[i].set_title(f'Distribution of {feature} in Cluster {cluster}', fontsize=10)
ax[i].set_xlabel(feature, fontsize=10)
ax[i].set_ylabel('Count', fontsize=10)
ax[i].tick_params(axis='x', rotation=45, labelsize=9)
# Highlight the peak range (example: print the mode for focus)
counts, bin_edges = np.histogram(cluster_data[cluster][feature].dropna(), bins=20)
mode_bin_index = np.argmax(counts)
mode_value = bin_edges[mode_bin_index] + (bin_edges[1] - bin_edges[0]) / 2 # Midpoint of the peak bin
# Add vertical line for the mode
ax[i].axvline(mode_value, color='r', linestyle='--', label=f'Mode (Bin Peak): {mode_value:.2f}')
ax[i].legend()
# Adjust layout and display
plt.tight_layout()
plt.show()
if underrepresented_clusters:
print('='*50)
print("\nDistribution Plots for Prominent Features in Underrepresented Clusters:")
print(prominent_features, "\n")
for cluster in underrepresented_clusters:
# Create figure with subplots (number of subplots matches the number of prominent features)
num_features = len(prominent_features)
fig, ax = plt.subplots(1, num_features, figsize=(5 * num_features, 5)) # Adjust figsize based on number of features
# Ensure ax is iterable even if num_features is 1
ax = ax.flatten() if num_features > 1 else [ax]
# Plot distribution for each prominent feature
for i, feature in enumerate(prominent_features):
sns.histplot(data=cluster_data[cluster], x=feature, bins=20, kde=True, ax=ax[i])
ax[i].set_title(f'Distribution of {feature} in Cluster {cluster}', fontsize=14)
ax[i].set_xlabel(feature, fontsize=10)
ax[i].set_ylabel('Count', fontsize=10)
ax[i].tick_params(axis='x', rotation=45, labelsize=9)
# Highlight the peak range
counts, bin_edges = np.histogram(cluster_data[cluster][feature].dropna(), bins=20)
mode_bin_index = np.argmax(counts)
mode_value = bin_edges[mode_bin_index] + (bin_edges[1] - bin_edges[0]) / 2 # Midpoint of the peak bin
# Add vertical line for the mode
ax[i].axvline(mode_value, color='r', linestyle='--', label=f'Mode (Bin Peak): {mode_value:.2f}')
ax[i].legend()
# Adjust layout and display
plt.tight_layout()
plt.show()
return overrepresented_clusters, underrepresented_clusters, cluster_data, centroid_df
overrepresented, underrepresented , clustring_data , centroids_df= difference_plot(preds, pred_customers, customers_pca, customer_sc.columns, pca_35, sc, kmean_model.cluster_centers_)
Overrepresented clusters (ratio > 1): Cluster 0: Customer Proportion = 6.73%, General Proportion = 4.44%, Ratio = 1.51 Cluster 1: Customer Proportion = 8.67%, General Proportion = 6.39%, Ratio = 1.36 Cluster 4: Customer Proportion = 0.24%, General Proportion = 0.10%, Ratio = 2.41 Cluster 6: Customer Proportion = 5.36%, General Proportion = 5.13%, Ratio = 1.05 Cluster 9: Customer Proportion = 10.29%, General Proportion = 5.46%, Ratio = 1.88 Cluster 13: Customer Proportion = 12.71%, General Proportion = 4.39%, Ratio = 2.89 Cluster 17: Customer Proportion = 28.50%, General Proportion = 5.74%, Ratio = 4.96 Cluster 18: Customer Proportion = 1.88%, General Proportion = 0.39%, Ratio = 4.86 Underrepresented clusters (ratio < 1): Cluster 2: Customer Proportion = 4.32%, General Proportion = 4.43%, Ratio = 0.97 Cluster 3: Customer Proportion = 1.18%, General Proportion = 4.70%, Ratio = 0.25 Cluster 5: Customer Proportion = 0.33%, General Proportion = 4.58%, Ratio = 0.07 Cluster 7: Customer Proportion = 1.90%, General Proportion = 5.54%, Ratio = 0.34 Cluster 8: Customer Proportion = 2.45%, General Proportion = 3.93%, Ratio = 0.62 Cluster 10: Customer Proportion = 1.00%, General Proportion = 2.78%, Ratio = 0.36 Cluster 11: Customer Proportion = 0.64%, General Proportion = 7.08%, Ratio = 0.09 Cluster 12: Customer Proportion = 0.50%, General Proportion = 5.45%, Ratio = 0.09 Cluster 14: Customer Proportion = 0.42%, General Proportion = 6.12%, Ratio = 0.07 Cluster 15: Customer Proportion = 4.90%, General Proportion = 6.02%, Ratio = 0.81 Cluster 16: Customer Proportion = 0.22%, General Proportion = 2.84%, Ratio = 0.08 Cluster 19: Customer Proportion = 3.07%, General Proportion = 3.97%, Ratio = 0.77 Cluster 20: Customer Proportion = 2.84%, General Proportion = 6.39%, Ratio = 0.44 Cluster 21: Customer Proportion = 1.85%, General Proportion = 4.11%, Ratio = 0.45 Distribution Plots for Prominent Features in Overrepresented Clusters: ['LP_STATUS_FEIN', 'LP_STATUS_GROB', 'FINANZ_MINIMALIST', 'MOBI_REGIO']
================================================== Distribution Plots for Prominent Features in Underrepresented Clusters: ['LP_STATUS_FEIN', 'LP_STATUS_GROB', 'FINANZ_MINIMALIST', 'MOBI_REGIO']
# What kinds of people are part of a cluster that is overrepresented in the
# customer data compared to the general population?
overrepresented
[0, 1, 4, 6, 9, 13, 17, 18]
# What kinds of people are part of a cluster that is underrepresented in the
# customer data compared to the general population?
underrepresented
[2, 3, 5, 7, 8, 10, 11, 12, 14, 15, 16, 19, 20, 21]
Discussion 3.3: Compare Customer Data to Demographics Data¶
Overrepresented clusters (ratio > 1):
Cluster 0: Customer Proportion = 6.73%, General Proportion = 4.44%, Ratio = 1.51
Cluster 1: Customer Proportion = 8.67%, General Proportion = 6.39%, Ratio = 1.36
Cluster 4: Customer Proportion = 0.24%, General Proportion = 0.10%, Ratio = 2.41
Cluster 6: Customer Proportion = 5.36%, General Proportion = 5.13%, Ratio = 1.05
Cluster 9: Customer Proportion = 10.29%, General Proportion = 5.46%, Ratio = 1.88
Cluster 13: Customer Proportion = 12.71%, General Proportion = 4.39%, Ratio = 2.89
Cluster 17: Customer Proportion = 28.50%, General Proportion = 5.74%, Ratio = 4.96
Cluster 18: Customer Proportion = 1.88%, General Proportion = 0.39%, Ratio = 4.86
Popular Segments
- Cluster 0:
- Proportions: Customer Proportion = 8.29%, General Proportion = 5.09%, Ratio = 1.63
- Characteristics:
- Social status: aspiring low-income earners,
- Social status : low-income earners
- Financial typology : average
- Movement patterns : high movement pattern
- Cluster 0:
So, we can decide the customer segment that relatively popular with the mail-order company. The customer with Social status aspiring low-income earners and low-income earners and Financial typology : average and Movement patterns : high movement pattern
Underrepresented clusters (ratio < 1):
- Cluster 2: Customer Proportion = 4.32%, General Proportion = 4.43%, Ratio = 0.97
- Cluster 3: Customer Proportion = 1.18%, General Proportion = 4.70%, Ratio = 0.25
- Cluster 5: Customer Proportion = 0.33%, General Proportion = 4.58%, Ratio = 0.07
- Cluster 7: Customer Proportion = 1.90%, General Proportion = 5.54%, Ratio = 0.34
- Cluster 8: Customer Proportion = 2.45%, General Proportion = 3.93%, Ratio = 0.62
- Cluster 10: Customer Proportion = 1.00%, General Proportion = 2.78%, Ratio = 0.36
- Cluster 11: Customer Proportion = 0.64%, General Proportion = 7.08%, Ratio = 0.09
- Cluster 12: Customer Proportion = 0.50%, General Proportion = 5.45%, Ratio = 0.09
- Cluster 14: Customer Proportion = 0.42%, General Proportion = 6.12%, Ratio = 0.07
- Cluster 15: Customer Proportion = 4.90%, General Proportion = 6.02%, Ratio = 0.81
- Cluster 16: Customer Proportion = 0.22%, General Proportion = 2.84%, Ratio = 0.08
- Cluster 19: Customer Proportion = 3.07%, General Proportion = 3.97%, Ratio = 0.77
- Cluster 20: Customer Proportion = 2.84%, General Proportion = 6.39%, Ratio = 0.44
- Cluster 21: Customer Proportion = 1.85%, General Proportion = 4.11%, Ratio = 0.45
Popular Segments
- Cluster 2:
- Proportions: Customer Proportion = 8.29%, General Proportion = 5.09%, Ratio = 1.63
- Characteristics:
- Social status : new houseowners and houseowners So,we can decide the customer segment that relatively unpopular with the mail-order company. A customer who new houseowners and houseowners and low Financial typology and Movement patterns : low movement pattern and very low movement
- Cluster 2:
Congratulations on making it this far in the project! Before you finish, make sure to check through the entire notebook from top to bottom to make sure that your analysis follows a logical flow and all of your findings are documented in Discussion cells. Once you've checked over all of your work, you should export the notebook as an HTML document to submit for evaluation. You can do this from the menu, navigating to File -> Download as -> HTML (.html). You will submit both that document and this notebook for your project submission.